From d29b0cc8f1ac4f6d873741cc883cac6f33c18df3 Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Tue, 21 Jan 2020 16:52:20 +0200 Subject: [PATCH 001/156] Use GHC-8.8.2 on Travis --- .travis.yml | 4 ++-- doc/cookbook/basic-auth/basic-auth.cabal | 2 +- doc/cookbook/basic-streaming/basic-streaming.cabal | 2 +- doc/cookbook/curl-mock/curl-mock.cabal | 2 +- doc/cookbook/db-postgres-pool/db-postgres-pool.cabal | 2 +- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- doc/cookbook/file-upload/file-upload.cabal | 2 +- doc/cookbook/generic/generic.cabal | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 2 +- doc/cookbook/https/https.cabal | 2 +- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- doc/cookbook/pagination/pagination.cabal | 2 +- doc/cookbook/sentry/sentry.cabal | 2 +- doc/cookbook/structuring-apis/structuring-apis.cabal | 2 +- doc/cookbook/testing/testing.cabal | 2 +- doc/cookbook/using-custom-monad/using-custom-monad.cabal | 2 +- doc/cookbook/using-free-client/using-free-client.cabal | 2 +- doc/tutorial/tutorial.cabal | 2 +- servant-client-core/servant-client-core.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-conduit/servant-conduit.cabal | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-machines/servant-machines.cabal | 2 +- servant-pipes/servant-pipes.cabal | 2 +- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 28 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4f839a90..632806a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,8 +37,8 @@ jobs: - 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.1 - 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.1","cabal-install-3.0"]}} + - 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"]}} diff --git a/doc/cookbook/basic-auth/basic-auth.cabal b/doc/cookbook/basic-auth/basic-auth.cabal index c7112e2f..1df7d086 100644 --- a/doc/cookbook/basic-auth/basic-auth.cabal +++ b/doc/cookbook/basic-auth/basic-auth.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-basic-auth main-is: BasicAuth.lhs diff --git a/doc/cookbook/basic-streaming/basic-streaming.cabal b/doc/cookbook/basic-streaming/basic-streaming.cabal index 07329d7a..714989b5 100644 --- a/doc/cookbook/basic-streaming/basic-streaming.cabal +++ b/doc/cookbook/basic-streaming/basic-streaming.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-basic-streaming main-is: Streaming.lhs diff --git a/doc/cookbook/curl-mock/curl-mock.cabal b/doc/cookbook/curl-mock/curl-mock.cabal index 0b2a221a..7fabc1cc 100644 --- a/doc/cookbook/curl-mock/curl-mock.cabal +++ b/doc/cookbook/curl-mock/curl-mock.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbock-curl-mock main-is: CurlMock.lhs diff --git a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal index 10719272..41013fd1 100644 --- a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal +++ b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-db-postgres-pool main-is: PostgresPool.lhs diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index 9f4e2004..cd0cb002 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-db-sqlite-simple main-is: DBConnection.lhs diff --git a/doc/cookbook/file-upload/file-upload.cabal b/doc/cookbook/file-upload/file-upload.cabal index e97773d3..b9953bff 100644 --- a/doc/cookbook/file-upload/file-upload.cabal +++ b/doc/cookbook/file-upload/file-upload.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-file-upload main-is: FileUpload.lhs diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal index a5908beb..3a94c0ac 100644 --- a/doc/cookbook/generic/generic.cabal +++ b/doc/cookbook/generic/generic.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-using-custom-monad main-is: Generic.lhs diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index 66407a45..b2aeef30 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -11,7 +11,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-hoist-server-with-context main-is: HoistServerWithContext.lhs diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index b0d793b6..080a9407 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-https main-is: Https.lhs diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index fd5cdbdf..51724347 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -11,7 +11,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-jwt-and-basic-auth main-is: JWTAndBasicAuth.lhs diff --git a/doc/cookbook/pagination/pagination.cabal b/doc/cookbook/pagination/pagination.cabal index 387c5dec..74a47d7b 100644 --- a/doc/cookbook/pagination/pagination.cabal +++ b/doc/cookbook/pagination/pagination.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-pagination main-is: Pagination.lhs diff --git a/doc/cookbook/sentry/sentry.cabal b/doc/cookbook/sentry/sentry.cabal index 30b31cd5..03d0acc4 100644 --- a/doc/cookbook/sentry/sentry.cabal +++ b/doc/cookbook/sentry/sentry.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-sentry main-is: Sentry.lhs diff --git a/doc/cookbook/structuring-apis/structuring-apis.cabal b/doc/cookbook/structuring-apis/structuring-apis.cabal index f6fb631c..810a69ad 100644 --- a/doc/cookbook/structuring-apis/structuring-apis.cabal +++ b/doc/cookbook/structuring-apis/structuring-apis.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-structuring-apis main-is: StructuringApis.lhs diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index 28ed9691..6ba8f785 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -10,7 +10,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-testing main-is: Testing.lhs diff --git a/doc/cookbook/using-custom-monad/using-custom-monad.cabal b/doc/cookbook/using-custom-monad/using-custom-monad.cabal index 701d5116..232c8d8b 100644 --- a/doc/cookbook/using-custom-monad/using-custom-monad.cabal +++ b/doc/cookbook/using-custom-monad/using-custom-monad.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-using-custom-monad main-is: UsingCustomMonad.lhs diff --git a/doc/cookbook/using-free-client/using-free-client.cabal b/doc/cookbook/using-free-client/using-free-client.cabal index d50bd419..c8a07f53 100644 --- a/doc/cookbook/using-free-client/using-free-client.cabal +++ b/doc/cookbook/using-free-client/using-free-client.cabal @@ -8,7 +8,7 @@ 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.1 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2 executable cookbook-using-free-client main-is: UsingFreeClient.lhs diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index ce245b65..b338180d 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -17,7 +17,7 @@ tested-with: GHC==8.2.2 GHC==8.4.4 GHC==8.6.5 - GHC==8.8.1 + GHC==8.8.2 extra-source-files: static/index.html static/ui.js diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 3faf65bb..7d949584 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -21,7 +21,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 , GHCJS == 8.4 extra-source-files: diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index dd7356f1..0eb9152c 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -25,7 +25,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 4aec51d8..8c1abaaa 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -22,7 +22,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 72896d72..293cf514 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -24,7 +24,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 1d85c132..32693593 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -27,7 +27,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 5621ba67..977b44ea 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -25,7 +25,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 31f1de7d..293c109f 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -22,7 +22,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 5fdc4b5a..bfdd2213 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -21,7 +21,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index a3cadcaf..12882acf 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -28,7 +28,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 extra-source-files: CHANGELOG.md diff --git a/servant/servant.cabal b/servant/servant.cabal index aef26744..783099a9 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -25,7 +25,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.1 + || ==8.8.2 , GHCJS == 8.4 extra-source-files: From 3bf4b100a825c95c0674d7860b3f7e735c00f8ea Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Thu, 23 Jan 2020 12:31:41 +0200 Subject: [PATCH 002/156] Bump version to 0.17 --- .travis.yml | 58 ++++++----- cabal.project | 5 + servant-client-core/servant-client-core.cabal | 4 +- servant-client/servant-client.cabal | 10 +- servant-conduit/servant-conduit.cabal | 8 +- servant-docs/servant-docs.cabal | 4 +- servant-foreign/servant-foreign.cabal | 5 +- .../servant-http-streams.cabal | 10 +- servant-machines/servant-machines.cabal | 8 +- servant-pipes/servant-pipes.cabal | 7 +- servant-server/servant-server.cabal | 2 +- servant/CHANGELOG.md | 97 +++++++++++++++++++ servant/servant.cabal | 2 +- 13 files changed, 160 insertions(+), 60 deletions(-) diff --git a/.travis.yml b/.travis.yml index 632806a8..6fd6ef9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ # # For more information, see https://github.com/haskell-CI/haskell-ci # -# version: 0.9.20200110 +# version: 0.9.20200121 # version: ~> 1.0 language: c @@ -75,39 +75,31 @@ before_install: - echo $HCNUMVER - CABAL="$CABAL -vnormal+nowrap" - set -o pipefail -install: - - ${CABAL} --version - - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" - - node --version - - echo $GHCJS - 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 - echo " secure: True" >> $CABALHOME/config - echo " key-threshold: 3" >> $CABALHOME/config - echo " root-keys:" >> $CABALHOME/config - echo " fe331502606802feac15e514d9b9ea83fee8b6ffef71335479a2e68d84adc6b0" >> $CABALHOME/config - echo " 1ea9ba32c526d1cc91ab5e5bd364ec5e9e8cb67179a471872f6e26f0ae773d42" >> $CABALHOME/config - echo " 2c6c3627bd6c982990239487f1abd02e08a02e6cf16edb105a8012d444d870c3" >> $CABALHOME/config - echo " 0a5c7ea47cd1b15f01f5f51a33adda7e655bc0f0b0615baa8e271f4c3351e21d" >> $CABALHOME/config - echo " 51f0161b906011b52c6613376b1ae937670da69322113a246a09f807c62f6921" >> $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 @@ -157,6 +149,9 @@ install: echo "allow-newer: io-streams-1.5.1.0:network" >> cabal.project echo "allow-newer: io-streams-1.5.1.0:primitive" >> cabal.project echo "allow-newer: openssl-streams-1.2.2.0:network" >> cabal.project + echo "allow-newer: servant" >> cabal.project + echo "allow-newer: servant-server" >> cabal.project + echo "allow-newer: servant-client" >> 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 @@ -259,6 +254,9 @@ script: echo "allow-newer: io-streams-1.5.1.0:network" >> cabal.project echo "allow-newer: io-streams-1.5.1.0:primitive" >> cabal.project echo "allow-newer: openssl-streams-1.2.2.0:network" >> cabal.project + echo "allow-newer: servant" >> cabal.project + echo "allow-newer: servant-server" >> cabal.project + echo "allow-newer: servant-client" >> 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 @@ -318,5 +316,5 @@ script: - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all ; fi - echo -en 'travis_fold:end:haddock\\r' -# REGENDATA ("0.9.20200110",["--config=cabal.haskell-ci","--output=.travis.yml","cabal.project"]) +# REGENDATA ("0.9.20200121",["--config=cabal.haskell-ci","--output=.travis.yml","cabal.project"]) # EOF diff --git a/cabal.project b/cabal.project index 4f29900b..f31d0ecc 100644 --- a/cabal.project +++ b/cabal.project @@ -65,3 +65,8 @@ constraints: base-compat ^>=0.11 -- needed for doctests write-ghc-environment-files: always + +-- servant-0.17 +allow-newer: servant +allow-newer: servant-server +allow-newer: servant-client diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 7d949584..3d1055db 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client-core -version: 0.16 +version: 0.17 synopsis: Core functionality and class for client function generation for servant APIs category: Servant, Web @@ -69,7 +69,7 @@ library -- Servant dependencies build-depends: - servant >= 0.16 && <0.17 + servant >= 0.17 && <0.18 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 0eb9152c..bdbecc0c 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client -version: 0.16.0.1 +version: 0.17 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -62,8 +62,8 @@ library -- Servant dependencies. -- Strict dependency on `servant-client-core` as we re-export things. build-depends: - servant == 0.16.* - , servant-client-core >= 0.16 && <0.16.1 + servant == 0.17.* + , servant-client-core >= 0.17 && <0.17.1 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. @@ -127,8 +127,8 @@ test-suite spec , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 , QuickCheck >= 2.12.6.1 && < 2.14 - , servant == 0.16.* - , servant-server == 0.16.* + , servant == 0.17.* + , servant-server == 0.17.* , tdigest >= 0.2 && < 0.3 build-tool-depends: diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 8c1abaaa..154af5db 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -1,7 +1,7 @@ cabal-version: >=1.10 name: servant-conduit version: 0.15 -x-revision: 1 +x-revision: 3 synopsis: Servant Stream support for conduit. category: Servant, Web, Enumerator @@ -39,7 +39,7 @@ library , conduit >=1.3.1 && <1.4 , mtl >=2.2.2 && <2.3 , resourcet >=1.2.2 && <1.3 - , servant >=0.15 && <0.17 + , servant >=0.15 && <0.18 , unliftio-core >=0.1.2.0 && <0.2 hs-source-dirs: src default-language: Haskell2010 @@ -60,8 +60,8 @@ test-suite example , resourcet , servant , servant-conduit - , servant-server >=0.15 && <0.17 - , servant-client >=0.15 && <0.17 + , servant-server >=0.15 && <0.18 + , servant-client >=0.15 && <0.18 , wai >=3.2.1.2 && <3.3 , warp >=3.2.25 && <3.4 , http-client diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 293cf514..d5912394 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-docs -version: 0.11.4 +version: 0.11.5 synopsis: generate API docs for your servant webservice category: Servant, Web @@ -52,7 +52,7 @@ library -- Servant dependencies build-depends: - servant >= 0.15 && <0.17 + servant >= 0.17 && <0.18 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 32693593..399e3870 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -1,7 +1,6 @@ cabal-version: >=1.10 name: servant-foreign -version: 0.15 -x-revision: 1 +version: 0.15.1 synopsis: Helpers for generating clients for servant APIs in any programming language category: Servant, Web @@ -52,7 +51,7 @@ library -- Servant dependencies build-depends: - servant >=0.15 && <0.17 + servant >=0.17 && <0.18 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 977b44ea..3a9ee9c5 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-http-streams -version: 0.16 +version: 0.17 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -59,8 +59,8 @@ library -- Servant dependencies. -- Strict dependency on `servant-client-core` as we re-export things. build-depends: - servant == 0.16.* - , servant-client-core >= 0.16 && <0.16.1 + servant == 0.17.* + , servant-client-core >= 0.17 && <0.17.1 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. @@ -121,8 +121,8 @@ test-suite spec , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 , QuickCheck >= 2.12.6.1 && < 2.14 - , servant == 0.16.* - , servant-server == 0.16.* + , servant == 0.17.* + , servant-server == 0.17.* , tdigest >= 0.2 && < 0.3 build-tool-depends: diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 293c109f..0603ff58 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -1,7 +1,7 @@ cabal-version: >=1.10 name: servant-machines version: 0.15 -x-revision: 1 +x-revision: 4 synopsis: Servant Stream support for machines category: Servant, Web, Enumerator @@ -38,7 +38,7 @@ library , bytestring >=0.10.8.1 && <0.11 , machines >=0.6.4 && <0.8 , mtl >=2.2.2 && <2.3 - , servant >=0.15 && <0.17 + , servant >=0.15 && <0.18 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall @@ -57,8 +57,8 @@ test-suite example , servant , machines , servant-machines - , servant-server >=0.15 && <0.17 - , servant-client >=0.15 && <0.17 + , servant-server >=0.15 && <0.18 + , servant-client >=0.15 && <0.18 , wai >=3.2.1.2 && <3.3 , warp >=3.2.25 && <3.4 , http-client diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index bfdd2213..79ca52f4 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -1,6 +1,7 @@ cabal-version: >=1.10 name: servant-pipes version: 0.15.1 +x-revision: 1 synopsis: Servant Stream support for pipes category: Servant, Web, Pipes @@ -39,7 +40,7 @@ library , pipes-safe >=2.3.1 && <2.4 , mtl >=2.2.2 && <2.3 , monad-control >=1.0.2.3 && <1.1 - , servant >=0.15 && <0.17 + , servant >=0.15 && <0.18 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall @@ -60,8 +61,8 @@ test-suite example , pipes-safe , servant-pipes , pipes-bytestring >=2.1.6 && <2.2 - , servant-server >=0.15 && <0.17 - , servant-client >=0.15 && <0.17 + , servant-server >=0.15 && <0.18 + , servant-client >=0.15 && <0.18 , wai >=3.2.1.2 && <3.3 , warp >=3.2.25 && <3.4 , http-client diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 12882acf..bcd8c436 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-server -version: 0.16 +version: 0.17 synopsis: A family of combinators for defining webservices APIs and serving them category: Servant, Web diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index 8414624d..254d1866 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -1,5 +1,102 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.17 +---- + +### Significant changes + +- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228) + + The `NoContent` API endpoints should now use `NoContentVerb` combinator. + The API type changes are usually of the kind + + ```diff + - :<|> PostNoContent '[JSON] NoContent + + :<|> PostNoContent + ``` + + i.e. one doesn't need to specify the content-type anymore. There is no content. + +- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156) + + You can specify a lenient capture as + + ```haskell + :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET + ``` + + which will make the capture always succeed. Handlers will be of the + type `Either String CapturedType`, where `Left err` represents + the possible parse failure. + +- *servant-client* Added a function to create Client.Request in ClientEnv [#1213](https://github.com/haskell-servant/servant/pull/1213) [#1255](https://github.com/haskell-servant/servant/pull/1255) + + The new member `makeClientRequest` of `ClientEnv` is used to create + `http-client` `Request` from `servant-client-core` `Request`. + This functionality can be used for example to set + dynamic timeouts for each request. + +- *servant-server* use queryString to parse QueryParam, QueryParams and QueryFlag [#1249](https://github.com/haskell-servant/servant/pull/1249) [#1262](https://github.com/haskell-servant/servant/pull/1262) + + Some APIs need query parameters rewriting, e.g. in order to support + for multiple casing (camel, snake, etc) or something to that effect. + + This could be easily achieved by using WAI Middleware and modyfing + request's `Query`. But QueryParam, QueryParams and QueryFlag use + `rawQueryString`. By using `queryString` rather then `rawQueryString` + we can enable such rewritings. + +- *servant* *servant-server* Make packages `build-type: Simple` [#1263](https://github.com/haskell-servant/servant/pull/1263) + + We used `build-type: Custom`, but it's problematic e.g. + for cross-compiling. The benefit is small, as the doctests + can be run other ways too (though not so conviniently). + +### Other changes + +- *servant-client* *servant-client-core* *servant-http-streams* Fix Verb with headers checking content type differently [#1200](https://github.com/haskell-servant/servant/issues/1200) [#1204](https://github.com/haskell-servant/servant/pull/1204) + + For `Verb`s with response `Headers`, the implementation didn't check + for the content-type of the response. Now it does. + +- *servant-docs* Merge documentation from duplicate routes [#1240](https://github.com/haskell-servant/servant/issues/1240) [#1241](https://github.com/haskell-servant/servant/pull/1241) + + Servant supports defining the same route multiple times with different + content-types and result-types, but servant-docs was only documenting + the first of copy of such duplicated routes. It now combines the + documentation from all the copies. + + Unfortunately, it is not yet possible for the documentation to specify + multiple status codes. + +- Add sponsorship button [#1190](https://github.com/haskell-servant/servant/pull/1190) + + [Well-Typed](https://www.well-typed.com/) is a consultancy which could help you with `servant` issues + (See consultancies section on https://www.servant.dev/). + +- Try changelog-d for changelog management [#1230](https://github.com/haskell-servant/servant/pull/1230) + + Check the [CONTRIBUTING.md](https://github.com/haskell-servant/servant/blob/master/CONTRIBUTING.md) for details + +- CI and testing tweaks. [#1154](https://github.com/haskell-servant/servant/pull/1154) [#1157](https://github.com/haskell-servant/servant/pull/1157) [#1182](https://github.com/haskell-servant/servant/pull/1182) [#1214](https://github.com/haskell-servant/servant/pull/1214) [#1229](https://github.com/haskell-servant/servant/pull/1229) [#1233](https://github.com/haskell-servant/servant/pull/1233) [#1242](https://github.com/haskell-servant/servant/pull/1242) [#1247](https://github.com/haskell-servant/servant/pull/1247) [#1250](https://github.com/haskell-servant/servant/pull/1250) [#1258](https://github.com/haskell-servant/servant/pull/1258) + + We are experiencing some bitrotting of cookbook recipe dependencies, + therefore some of them aren't build as part of our CI anymore. + +- New cookbook recipes [#1088](https://github.com/haskell-servant/servant/pull/1088) [#1171](https://github.com/haskell-servant/servant/pull/1171) [#1198](https://github.com/haskell-servant/servant/pull/1198) + + - [OIDC Recipe](#TODO) + - [MySQL Recipe](#TODO) + +- *servant-jsaddle* Progress on servant-jsaddle [#1216](https://github.com/haskell-servant/servant/pull/1216) +- *servant-docs* Prevent race-conditions in testing [#1194](https://github.com/haskell-servant/servant/pull/1194) +- *servant-client* *servant-http-streams* `HasClient` instance for `Stream` with `Headers` [#1170](https://github.com/haskell-servant/servant/issues/1170) [#1197](https://github.com/haskell-servant/servant/pull/1197) +- *servant* Remove unused extensions from cabal file [#1201](https://github.com/haskell-servant/servant/pull/1201) +- *servant-client* Redact the authorization header in Show and exceptions [#1238](https://github.com/haskell-servant/servant/pull/1238) +- Dependency upgrades [#1173](https://github.com/haskell-servant/servant/pull/1173) [#1181](https://github.com/haskell-servant/servant/pull/1181) [#1183](https://github.com/haskell-servant/servant/pull/1183) [#1188](https://github.com/haskell-servant/servant/pull/1188) [#1224](https://github.com/haskell-servant/servant/pull/1224) [#1245](https://github.com/haskell-servant/servant/pull/1245) [#1257](https://github.com/haskell-servant/servant/pull/1257) +- Documentation updates [#1162](https://github.com/haskell-servant/servant/pull/1162) [#1174](https://github.com/haskell-servant/servant/pull/1174) [#1175](https://github.com/haskell-servant/servant/pull/1175) [#1234](https://github.com/haskell-servant/servant/pull/1234) [#1244](https://github.com/haskell-servant/servant/pull/1244) [#1247](https://github.com/haskell-servant/servant/pull/1247) + + 0.16.2 ------ diff --git a/servant/servant.cabal b/servant/servant.cabal index 783099a9..9a571d45 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant -version: 0.16 +version: 0.17 synopsis: A family of combinators for defining webservices APIs category: Servant, Web From 8fc47edf99b1fd0e9843958b73acee2f577966e1 Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Thu, 23 Jan 2020 13:14:24 +0200 Subject: [PATCH 003/156] Remove deprecated modules (end of 2019 is passed) --- servant/CHANGELOG.md | 5 +++++ servant/servant.cabal | 5 ----- servant/src/Servant/API/Internal/Test/ComprehensiveAPI.hs | 6 ------ servant/src/Servant/Utils/Links.hs | 6 ------ 4 files changed, 5 insertions(+), 17 deletions(-) delete mode 100644 servant/src/Servant/API/Internal/Test/ComprehensiveAPI.hs delete mode 100644 servant/src/Servant/Utils/Links.hs diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index 254d1866..6fecc9c1 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -52,6 +52,11 @@ for cross-compiling. The benefit is small, as the doctests can be run other ways too (though not so conviniently). +- *servant* Remove deprecated modules [1268#](https://github.com/haskell-servant/servant/pull/1268) + + - `Servant.Utils.Links` is `Servant.Links` + - `Servant.API.Internal.Test.ComprehensiveAPI` is `Servant.Test.ComprehensiveAPI` + ### Other changes - *servant-client* *servant-client-core* *servant-http-streams* Fix Verb with headers checking content type differently [#1200](https://github.com/haskell-servant/servant/issues/1200) [#1204](https://github.com/haskell-servant/servant/pull/1204) diff --git a/servant/servant.cabal b/servant/servant.cabal index 9a571d45..05f9113a 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -74,11 +74,6 @@ library exposed-modules: Servant.Links - -- Deprecated modules, to be removed in late 2019 - exposed-modules: - Servant.Utils.Links - Servant.API.Internal.Test.ComprehensiveAPI - -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 -- diff --git a/servant/src/Servant/API/Internal/Test/ComprehensiveAPI.hs b/servant/src/Servant/API/Internal/Test/ComprehensiveAPI.hs deleted file mode 100644 index ee2609ca..00000000 --- a/servant/src/Servant/API/Internal/Test/ComprehensiveAPI.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Servant.API.Internal.Test.ComprehensiveAPI - {-# DEPRECATED "Use Servant.TestComprehensiveAPI" #-} - ( module Servant.Test.ComprehensiveAPI ) - where - -import Servant.Test.ComprehensiveAPI diff --git a/servant/src/Servant/Utils/Links.hs b/servant/src/Servant/Utils/Links.hs deleted file mode 100644 index dc6d1b71..00000000 --- a/servant/src/Servant/Utils/Links.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Servant.Utils.Links - {-# DEPRECATED "Use Servant.Links." #-} - ( module Servant.Links ) - where - -import Servant.Links From b519014f96d49911e864f48f7fb57ddaa03c51cd Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Thu, 23 Jan 2020 13:43:19 +0200 Subject: [PATCH 004/156] Update other changelogs --- servant-client-core/CHANGELOG.md | 39 +++++++++++++++++++ servant-client/CHANGELOG.md | 48 +++++++++++++++++++++++ servant-docs/CHANGELOG.md | 40 ++++++++++++++++++++ servant-foreign/CHANGELOG.md | 63 +++++++++++++++++++++++++++++++ servant-http-streams/CHANGELOG.md | 41 ++++++++++++++++++++ servant-server/CHANGELOG.md | 45 ++++++++++++++++++++++ 6 files changed, 276 insertions(+) diff --git a/servant-client-core/CHANGELOG.md b/servant-client-core/CHANGELOG.md index 9dfcadc3..9fa1ac36 100644 --- a/servant-client-core/CHANGELOG.md +++ b/servant-client-core/CHANGELOG.md @@ -1,6 +1,45 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.17 +---- + +### Significant changes + +- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228) + + The `NoContent` API endpoints should now use `NoContentVerb` combinator. + The API type changes are usually of the kind + + ```diff + - :<|> PostNoContent '[JSON] NoContent + + :<|> PostNoContent + ``` + + i.e. one doesn't need to specify the content-type anymore. There is no content. + +- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156) + + You can specify a lenient capture as + + ```haskell + :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET + ``` + + which will make the capture always succeed. Handlers will be of the + type `Either String CapturedType`, where `Left err` represents + the possible parse failure. + +### Other changes + +- *servant-client* *servant-client-core* *servant-http-streams* Fix Verb with headers checking content type differently [#1200](https://github.com/haskell-servant/servant/issues/1200) [#1204](https://github.com/haskell-servant/servant/pull/1204) + + For `Verb`s with response `Headers`, the implementation didn't check + for the content-type of the response. Now it does. + +- *servant-client* *servant-http-streams* `HasClient` instance for `Stream` with `Headers` [#1170](https://github.com/haskell-servant/servant/issues/1170) [#1197](https://github.com/haskell-servant/servant/pull/1197) +- *servant-client* Redact the authorization header in Show and exceptions [#1238](https://github.com/haskell-servant/servant/pull/1238) + 0.16 ---- diff --git a/servant-client/CHANGELOG.md b/servant-client/CHANGELOG.md index 02f7bd85..362ba567 100644 --- a/servant-client/CHANGELOG.md +++ b/servant-client/CHANGELOG.md @@ -1,6 +1,54 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.17 +---- + +### Significant changes + +- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228) + + The `NoContent` API endpoints should now use `NoContentVerb` combinator. + The API type changes are usually of the kind + + ```diff + - :<|> PostNoContent '[JSON] NoContent + + :<|> PostNoContent + ``` + + i.e. one doesn't need to specify the content-type anymore. There is no content. + +- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156) + + You can specify a lenient capture as + + ```haskell + :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET + ``` + + which will make the capture always succeed. Handlers will be of the + type `Either String CapturedType`, where `Left err` represents + the possible parse failure. + +- *servant-client* Added a function to create Client.Request in ClientEnv [#1213](https://github.com/haskell-servant/servant/pull/1213) [#1255](https://github.com/haskell-servant/servant/pull/1255) + + The new member `makeClientRequest` of `ClientEnv` is used to create + `http-client` `Request` from `servant-client-core` `Request`. + This functionality can be used for example to set + dynamic timeouts for each request. + +### Other changes + +- *servant-client* *servant-client-core* *servant-http-streams* Fix Verb with headers checking content type differently [#1200](https://github.com/haskell-servant/servant/issues/1200) [#1204](https://github.com/haskell-servant/servant/pull/1204) + + For `Verb`s with response `Headers`, the implementation didn't check + for the content-type of the response. Now it does. + +- *servant-client* *servant-http-streams* `HasClient` instance for `Stream` with `Headers` [#1170](https://github.com/haskell-servant/servant/issues/1170) [#1197](https://github.com/haskell-servant/servant/pull/1197) +- *servant-client* Redact the authorization header in Show and exceptions [#1238](https://github.com/haskell-servant/servant/pull/1238) + + + 0.16.0.1 -------- diff --git a/servant-docs/CHANGELOG.md b/servant-docs/CHANGELOG.md index 5744be55..b11e1ffb 100644 --- a/servant-docs/CHANGELOG.md +++ b/servant-docs/CHANGELOG.md @@ -1,6 +1,46 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-docs/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.14.2 +---- + + +- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228) + + The `NoContent` API endpoints should now use `NoContentVerb` combinator. + The API type changes are usually of the kind + + ```diff + - :<|> PostNoContent '[JSON] NoContent + + :<|> PostNoContent + ``` + + i.e. one doesn't need to specify the content-type anymore. There is no content. + +- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156) + + You can specify a lenient capture as + + ```haskell + :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET + ``` + + which will make the capture always succeed. Handlers will be of the + type `Either String CapturedType`, where `Left err` represents + the possible parse failure. + +- *servant-docs* Merge documentation from duplicate routes [#1240](https://github.com/haskell-servant/servant/issues/1240) [#1241](https://github.com/haskell-servant/servant/pull/1241) + + Servant supports defining the same route multiple times with different + content-types and result-types, but servant-docs was only documenting + the first of copy of such duplicated routes. It now combines the + documentation from all the copies. + + Unfortunately, it is not yet possible for the documentation to specify + multiple status codes. + +- *servant-docs* Prevent race-conditions in testing [#1194](https://github.com/haskell-servant/servant/pull/1194) + 0.11.4 ------ diff --git a/servant-foreign/CHANGELOG.md b/servant-foreign/CHANGELOG.md index 64fc579b..c6bba80f 100644 --- a/servant-foreign/CHANGELOG.md +++ b/servant-foreign/CHANGELOG.md @@ -1,6 +1,69 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-foreign/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.15.1 +------ + +* Support `servant-0.17` + +0.15 +---- + +- *servant-foreign* Add support so `HasForeign` can be implemented for + `MultipartForm` from [`servant-multipart`](http://hackage.haskell.org/package/servant-multipart) + [#1035](https://github.com/haskell-servant/servant/pull/1035) + +- Drop support for GHC older than 8.0 + [#1008](https://github.com/haskell-servant/servant/pull/1008) + [#1009](https://github.com/haskell-servant/servant/pull/1009) + + +0.11.1 +------ + +- Add missing `Semigroup` instances + +0.11 +---- + +### Breaking changes + +- *servant* Add `Servant.API.Modifiers` + ([#873](https://github.com/haskell-servant/servant/pull/873)) +- Make foreign client Header arguments have the representation of 'Maybe' in those languages + ([#843](https://github.com/haskell-servant/servant/pull/843)) + +0.10.2 +------ + +### Changes + +* Add instances for `Description` and `Summary` combinators + ([#767](https://github.com/haskell-servant/servant/pull/767)) +* Derive Data for all types + ([#809](https://github.com/haskell-servant/servant/pull/809)) + +0.10.1 +------ + +### Changes + +* Don't drop samples in `HasDocs ReqBody` instance + ([#755](https://github.com/haskell-servant/servant/pull/755/files)). + *Breaking change in an `Internal` module*. + +0.10 +---- + +### Breaking changes + +* Do not apply JavaScript specific mangling to the names. + ([#191](https://github.com/haskell-servant/servant/issues/191)) + +0.7.1 +----- + +* Support GHC 8.0 0.15 ---- diff --git a/servant-http-streams/CHANGELOG.md b/servant-http-streams/CHANGELOG.md index 19b1f6b1..98619531 100644 --- a/servant-http-streams/CHANGELOG.md +++ b/servant-http-streams/CHANGELOG.md @@ -1,6 +1,47 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-http-streams/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.17 +---- + +### Significant changes + +- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228) + + The `NoContent` API endpoints should now use `NoContentVerb` combinator. + The API type changes are usually of the kind + + ```diff + - :<|> PostNoContent '[JSON] NoContent + + :<|> PostNoContent + ``` + + i.e. one doesn't need to specify the content-type anymore. There is no content. + +- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156) + + You can specify a lenient capture as + + ```haskell + :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET + ``` + + which will make the capture always succeed. Handlers will be of the + type `Either String CapturedType`, where `Left err` represents + the possible parse failure. + +### Other changes + +- *servant-client* *servant-client-core* *servant-http-streams* Fix Verb with headers checking content type differently [#1200](https://github.com/haskell-servant/servant/issues/1200) [#1204](https://github.com/haskell-servant/servant/pull/1204) + + For `Verb`s with response `Headers`, the implementation didn't check + for the content-type of the response. Now it does. + +- *servant-client* *servant-http-streams* `HasClient` instance for `Stream` with `Headers` [#1170](https://github.com/haskell-servant/servant/issues/1170) [#1197](https://github.com/haskell-servant/servant/pull/1197) +- *servant-client* Redact the authorization header in Show and exceptions [#1238](https://github.com/haskell-servant/servant/pull/1238) + + + 0.16.0.1 -------- diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index 22098411..436a9663 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -1,6 +1,51 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.17 +---- + +### Significant changes + +- Add NoContentVerb [#1028](https://github.com/haskell-servant/servant/issues/1028) [#1219](https://github.com/haskell-servant/servant/pull/1219) [#1228](https://github.com/haskell-servant/servant/pull/1228) + + The `NoContent` API endpoints should now use `NoContentVerb` combinator. + The API type changes are usually of the kind + + ```diff + - :<|> PostNoContent '[JSON] NoContent + + :<|> PostNoContent + ``` + + i.e. one doesn't need to specify the content-type anymore. There is no content. + +- `Capture` can be `Lenient` [#1155](https://github.com/haskell-servant/servant/issues/1155) [#1156](https://github.com/haskell-servant/servant/pull/1156) + + You can specify a lenient capture as + + ```haskell + :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET + ``` + + which will make the capture always succeed. Handlers will be of the + type `Either String CapturedType`, where `Left err` represents + the possible parse failure. + +- *servant-server* use queryString to parse QueryParam, QueryParams and QueryFlag [#1249](https://github.com/haskell-servant/servant/pull/1249) [#1262](https://github.com/haskell-servant/servant/pull/1262) + + Some APIs need query parameters rewriting, e.g. in order to support + for multiple casing (camel, snake, etc) or something to that effect. + + This could be easily achieved by using WAI Middleware and modyfing + request's `Query`. But QueryParam, QueryParams and QueryFlag use + `rawQueryString`. By using `queryString` rather then `rawQueryString` + we can enable such rewritings. + +- *servant* *servant-server* Make packages `build-type: Simple` [#1263](https://github.com/haskell-servant/servant/pull/1263) + + We used `build-type: Custom`, but it's problematic e.g. + for cross-compiling. The benefit is small, as the doctests + can be run other ways too (though not so conviniently). + 0.16.2 ------ From b318e69bffccfeacdbd59d7eae2f35135be805e3 Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Thu, 23 Jan 2020 13:43:46 +0200 Subject: [PATCH 005/156] Flush changelog-d --- changelog.d/issue1028 | 18 ------------------ changelog.d/issue1200 | 12 ------------ changelog.d/issue1240 | 16 ---------------- changelog.d/jsaddle | 3 --- changelog.d/pr1156 | 17 ----------------- changelog.d/pr1190 | 7 ------- changelog.d/pr1194 | 3 --- changelog.d/pr1197 | 4 ---- changelog.d/pr1201 | 3 --- changelog.d/pr1213 | 12 ------------ changelog.d/pr1238 | 3 --- changelog.d/pr1249 | 15 --------------- changelog.d/pr1263 | 11 ----------- changelog.d/z-changelog-d | 8 -------- changelog.d/z-ci-tweaks | 19 ------------------- changelog.d/z-cookbook | 9 --------- changelog.d/z-dependency-upgrades | 9 --------- changelog.d/z-documentation-updates | 8 -------- 18 files changed, 177 deletions(-) delete mode 100644 changelog.d/issue1028 delete mode 100644 changelog.d/issue1200 delete mode 100644 changelog.d/issue1240 delete mode 100644 changelog.d/jsaddle delete mode 100644 changelog.d/pr1156 delete mode 100644 changelog.d/pr1190 delete mode 100644 changelog.d/pr1194 delete mode 100644 changelog.d/pr1197 delete mode 100644 changelog.d/pr1201 delete mode 100644 changelog.d/pr1213 delete mode 100644 changelog.d/pr1238 delete mode 100644 changelog.d/pr1249 delete mode 100644 changelog.d/pr1263 delete mode 100644 changelog.d/z-changelog-d delete mode 100644 changelog.d/z-ci-tweaks delete mode 100644 changelog.d/z-cookbook delete mode 100644 changelog.d/z-dependency-upgrades delete mode 100644 changelog.d/z-documentation-updates diff --git a/changelog.d/issue1028 b/changelog.d/issue1028 deleted file mode 100644 index 73cede30..00000000 --- a/changelog.d/issue1028 +++ /dev/null @@ -1,18 +0,0 @@ -synopsis: Add NoContentVerb -prs: #1228 #1219 -issues: #1028 -significance: significant - -description: { - -The `NoContent` API endpoints should now use `NoContentVerb` combinator. -The API type changes are usually of the kind - -```diff -- :<|> PostNoContent '[JSON] NoContent -+ :<|> PostNoContent -``` - -i.e. one doesn't need to specify the content-type anymore. There is no content. - -} diff --git a/changelog.d/issue1200 b/changelog.d/issue1200 deleted file mode 100644 index 885a2571..00000000 --- a/changelog.d/issue1200 +++ /dev/null @@ -1,12 +0,0 @@ -synopsis: Fix Verb with headers checking content type differently -packages: servant-client-core servant-client -prs: #1204 -issues: #1200 -packages: servant-client servant-client-core servant-http-streams - -description: { - -For `Verb`s with response `Headers`, the implementation didn't check -for the content-type of the response. Now it does. - -} diff --git a/changelog.d/issue1240 b/changelog.d/issue1240 deleted file mode 100644 index dce11a69..00000000 --- a/changelog.d/issue1240 +++ /dev/null @@ -1,16 +0,0 @@ -synopsis: Merge documentation from duplicate routes -packages: servant-docs -prs: #1241 -issues: #1240 - -description: { - -Servant supports defining the same route multiple times with different -content-types and result-types, but servant-docs was only documenting -the first of copy of such duplicated routes. It now combines the -documentation from all the copies. - -Unfortunately, it is not yet possible for the documentation to specify -multiple status codes. - -} diff --git a/changelog.d/jsaddle b/changelog.d/jsaddle deleted file mode 100644 index 02dcf5b4..00000000 --- a/changelog.d/jsaddle +++ /dev/null @@ -1,3 +0,0 @@ -synopsis: Progress on servant-jsaddle -packages: servant-jsaddle -prs: #1216 diff --git a/changelog.d/pr1156 b/changelog.d/pr1156 deleted file mode 100644 index 91726ae9..00000000 --- a/changelog.d/pr1156 +++ /dev/null @@ -1,17 +0,0 @@ -synopsis: `Capture` can be `Lenient` -issues: #1155 -prs: #1156 -significance: significant -description: { - -You can specify a lenient capture as - -```haskell -:<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET -``` - -which will make the capture always succeed. Handlers will be of the -type `Either String CapturedType`, where `Left err` represents -the possible parse failure. - -} diff --git a/changelog.d/pr1190 b/changelog.d/pr1190 deleted file mode 100644 index e6a308c4..00000000 --- a/changelog.d/pr1190 +++ /dev/null @@ -1,7 +0,0 @@ -synopsis: Add sponsorship button -prs: #1190 -description: { - -[Well-Typed](https://www.well-typed.com/) - -} diff --git a/changelog.d/pr1194 b/changelog.d/pr1194 deleted file mode 100644 index 53136662..00000000 --- a/changelog.d/pr1194 +++ /dev/null @@ -1,3 +0,0 @@ -synopsis: Prevent race-conditions in testing -packages: servant-docs -prs: #1194 diff --git a/changelog.d/pr1197 b/changelog.d/pr1197 deleted file mode 100644 index bc041c62..00000000 --- a/changelog.d/pr1197 +++ /dev/null @@ -1,4 +0,0 @@ -synopsis: `HasClient` instance for `Stream` with `Headers` -packages: servant-client servant-client servant-http-streams -prs: #1197 -issues: #1170 diff --git a/changelog.d/pr1201 b/changelog.d/pr1201 deleted file mode 100644 index 0724fc38..00000000 --- a/changelog.d/pr1201 +++ /dev/null @@ -1,3 +0,0 @@ -synopsis: Remove unused extensions from cabal file -packages: servant -prs: #1201 diff --git a/changelog.d/pr1213 b/changelog.d/pr1213 deleted file mode 100644 index 3cb5f15b..00000000 --- a/changelog.d/pr1213 +++ /dev/null @@ -1,12 +0,0 @@ -synopsis: Added a function to create Client.Request in ClientEnv -packages: servant-client -significance: significant -prs: #1213 #1255 -description: { - -The new member `makeClientRequest` of `ClientEnv` is used to create -`http-client` `Request` from `servant-client-core` `Request`. -This functionality can be used for example to set -dynamic timeouts for each request. - -} diff --git a/changelog.d/pr1238 b/changelog.d/pr1238 deleted file mode 100644 index 5b0d838c..00000000 --- a/changelog.d/pr1238 +++ /dev/null @@ -1,3 +0,0 @@ -synopsis: Redact the authorization header in Show and exceptions -packages: servant-client -prs: #1238 diff --git a/changelog.d/pr1249 b/changelog.d/pr1249 deleted file mode 100644 index 3a5c772a..00000000 --- a/changelog.d/pr1249 +++ /dev/null @@ -1,15 +0,0 @@ -synopsis: use queryString to parse QueryParam, QueryParams and QueryFlag -packages: servant-server -prs: #1249 #1262 -significance: significant -description: { - -Some APIs need query parameters rewriting, e.g. in order to support - for multiple casing (camel, snake, etc) or something to that effect. - -This could be easily achieved by using WAI Middleware and modyfing -request's `Query`. But QueryParam, QueryParams and QueryFlag use -`rawQueryString`. By using `queryString` rather then `rawQueryString` -we can enable such rewritings. - -} diff --git a/changelog.d/pr1263 b/changelog.d/pr1263 deleted file mode 100644 index 593bcd1b..00000000 --- a/changelog.d/pr1263 +++ /dev/null @@ -1,11 +0,0 @@ -synopsis: Make packages `build-type: Simple` -packages: servant servant-server -prs: #1263 -significance: significant -description: { - -We used `build-type: Custom`, but it's problematic e.g. -for cross-compiling. The benefit is small, as the doctests -can be run other ways too (though not so conviniently). - -} diff --git a/changelog.d/z-changelog-d b/changelog.d/z-changelog-d deleted file mode 100644 index c7e027ff..00000000 --- a/changelog.d/z-changelog-d +++ /dev/null @@ -1,8 +0,0 @@ -synopsis: Try changelog-d for changelog management -prs: #1230 - -description: { - -Check the [CONTRIBUTING.md](https://github.com/haskell-servant/servant/blob/master/CONTRIBUTING.md) for details - -} diff --git a/changelog.d/z-ci-tweaks b/changelog.d/z-ci-tweaks deleted file mode 100644 index 331e5679..00000000 --- a/changelog.d/z-ci-tweaks +++ /dev/null @@ -1,19 +0,0 @@ -synopsis: CI and testing tweaks. -prs: - #1154 - #1157 - #1182 - #1214 - #1229 - #1233 - #1242 - #1247 - #1250 - #1258 - -description: { - -We are experiencing some bitrotting of cookbook recipe dependencies, -therefore some of them aren't build as part of our CI anymore. - -} diff --git a/changelog.d/z-cookbook b/changelog.d/z-cookbook deleted file mode 100644 index 71dca975..00000000 --- a/changelog.d/z-cookbook +++ /dev/null @@ -1,9 +0,0 @@ -synopsis: New cookbook recipes -prs: #1171 #1088 #1198 - -description: { - -- [OIDC Recipe](#TODO) -- [MySQL Recipe](#TODO) - -} diff --git a/changelog.d/z-dependency-upgrades b/changelog.d/z-dependency-upgrades deleted file mode 100644 index 43d72ef0..00000000 --- a/changelog.d/z-dependency-upgrades +++ /dev/null @@ -1,9 +0,0 @@ -synopsis: Dependency upgrades -prs: - #1173 - #1181 - #1183 - #1188 - #1224 - #1245 - #1257 diff --git a/changelog.d/z-documentation-updates b/changelog.d/z-documentation-updates deleted file mode 100644 index 3d8b2215..00000000 --- a/changelog.d/z-documentation-updates +++ /dev/null @@ -1,8 +0,0 @@ -synopsis: Documentation updates -prs: - #1162 - #1174 - #1175 - #1234 - #1244 - #1247 From b30286312f930aab070fd76f1ad13018a3b295aa Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Thu, 23 Jan 2020 14:10:20 +0200 Subject: [PATCH 006/156] Fix typo in servant-docs changelog --- servant-docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-docs/CHANGELOG.md b/servant-docs/CHANGELOG.md index b11e1ffb..10694590 100644 --- a/servant-docs/CHANGELOG.md +++ b/servant-docs/CHANGELOG.md @@ -1,7 +1,7 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-docs/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) -0.14.2 +0.11.5 ---- From c15f550e1fc3eb778d9d6cd1cb30fc26cdd61086 Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Thu, 23 Jan 2020 22:26:02 +0200 Subject: [PATCH 007/156] Incorrect lower bound in servant-server --- servant-server/servant-server.cabal | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index bcd8c436..97fa7d2e 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,6 +1,7 @@ cabal-version: >=1.10 name: servant-server version: 0.17 +x-revision: 1 synopsis: A family of combinators for defining webservices APIs and serving them category: Servant, Web @@ -74,7 +75,7 @@ library -- Servant dependencies -- strict dependency as we re-export 'servant' things. build-depends: - servant >= 0.16 && < 0.17.1 + servant >= 0.17 && < 0.17.1 , http-api-data >= 0.4.1 && < 0.4.2 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. From 791605111493770be5856165feb007cf2a6b443c Mon Sep 17 00:00:00 2001 From: Oleg Grenrus Date: Fri, 24 Jan 2020 12:54:38 +0200 Subject: [PATCH 008/156] Drop most allow-newer in cabal.project Dependencies are updated to allow servant-0.17 --- .travis.yml | 48 ++++++++++++++++-------------------------------- cabal.project | 22 +++++++--------------- 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6fd6ef9b..1341b9e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -137,22 +137,14 @@ install: 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: sqlite-simple < 0" >> cabal.project - echo "constraints: base-compat ^>=0.11" >> cabal.project - echo "allow-newer: aeson-pretty-0.8.7:base-compat" >> cabal.project - echo "allow-newer: vault-0.3.1.2:hashable" >> cabal.project - echo "allow-newer: psqueues-0.2.7.1:hashable" >> cabal.project - echo "allow-newer: sqlite-simple-0.4.16.0:semigroups" >> cabal.project - echo "allow-newer: direct-sqlite-2.3.24:semigroups" >> cabal.project - echo "allow-newer: io-streams-1.5.1.0:network" >> cabal.project - echo "allow-newer: io-streams-1.5.1.0:primitive" >> cabal.project - echo "allow-newer: openssl-streams-1.2.2.0:network" >> cabal.project - echo "allow-newer: servant" >> cabal.project - echo "allow-newer: servant-server" >> cabal.project - echo "allow-newer: servant-client" >> cabal.project - echo "optimization: False" >> cabal.project + 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 @@ -242,22 +234,14 @@ script: 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: sqlite-simple < 0" >> cabal.project - echo "constraints: base-compat ^>=0.11" >> cabal.project - echo "allow-newer: aeson-pretty-0.8.7:base-compat" >> cabal.project - echo "allow-newer: vault-0.3.1.2:hashable" >> cabal.project - echo "allow-newer: psqueues-0.2.7.1:hashable" >> cabal.project - echo "allow-newer: sqlite-simple-0.4.16.0:semigroups" >> cabal.project - echo "allow-newer: direct-sqlite-2.3.24:semigroups" >> cabal.project - echo "allow-newer: io-streams-1.5.1.0:network" >> cabal.project - echo "allow-newer: io-streams-1.5.1.0:primitive" >> cabal.project - echo "allow-newer: openssl-streams-1.2.2.0:network" >> cabal.project - echo "allow-newer: servant" >> cabal.project - echo "allow-newer: servant-server" >> cabal.project - echo "allow-newer: servant-client" >> cabal.project - echo "optimization: False" >> cabal.project + 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 diff --git a/cabal.project b/cabal.project index f31d0ecc..295938f1 100644 --- a/cabal.project +++ b/cabal.project @@ -47,26 +47,18 @@ constraints: foundation >=0.0.14, memory <0.14.12 || >0.14.12 -allow-newer: aeson-pretty-0.8.7:base-compat - -allow-newer: vault-0.3.1.2:hashable -allow-newer: psqueues-0.2.7.1:hashable -allow-newer: sqlite-simple-0.4.16.0:semigroups -allow-newer: direct-sqlite-2.3.24:semigroups -allow-newer: io-streams-1.5.1.0:network -allow-newer: io-streams-1.5.1.0:primitive -allow-newer: openssl-streams-1.2.2.0:network +constraints: base-compat ^>=0.11 +constraints: semigroups ^>=0.19 -- MonadFail -- https://github.com/nurpax/sqlite-simple/issues/74 constraints: sqlite-simple < 0 - -constraints: base-compat ^>=0.11 +-- 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 --- servant-0.17 -allow-newer: servant -allow-newer: servant-server -allow-newer: servant-client +-- 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 From b3b3dc9f4136f7400d2de61949c367d49e09d75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Choutri?= Date: Wed, 4 Mar 2020 15:53:37 +0100 Subject: [PATCH 009/156] docs(generic): Improve the documentation for `genericServerT` This commit adds an explanation and a link to the Servant Cookbook to `genericServerT`. Moreover, the `genericServer` and `genericServe`'s haddocks are slightly edited to add a missing 'a'. --- servant-server/src/Servant/Server/Generic.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/servant-server/src/Servant/Server/Generic.hs b/servant-server/src/Servant/Server/Generic.hs index c3a2e3b4..88dbe331 100644 --- a/servant-server/src/Servant/Server/Generic.hs +++ b/servant-server/src/Servant/Server/Generic.hs @@ -31,7 +31,7 @@ instance GenericMode (AsServerT m) where type AsServer = AsServerT Handler --- | Transform record of routes into a WAI 'Application'. +-- | Transform a record of routes into a WAI 'Application'. genericServe :: forall routes. ( HasServer (ToServantApi routes) '[] @@ -80,13 +80,17 @@ genericServeTWithContext f server ctx = p = genericApi (Proxy :: Proxy routes) pctx = Proxy :: Proxy ctx --- | Transform record of endpoints into a 'Server'. +-- | Transform a record of endpoints into a 'Server'. genericServer :: GenericServant routes AsServer => routes AsServer -> ToServant routes AsServer genericServer = toServant +-- | Transform a record of endpoints into a @'ServerT' m@. +-- +-- You can see an example usage of this function +-- . genericServerT :: GenericServant routes (AsServerT m) => routes (AsServerT m) From 8f60a02c25b21167bdb314a83275d8ebce05d946 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Thu, 2 Apr 2020 06:58:22 +0800 Subject: [PATCH 010/156] Allow QuickCheck 2.14 Builds fine and all tests pass. --- servant/servant.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servant/servant.cabal b/servant/servant.cabal index 05f9113a..9425039e 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -105,7 +105,7 @@ library , http-types >= 0.12.2 && < 0.13 , mmorph >= 1.1.2 && < 1.2 , network-uri >= 2.6.1.0 && < 2.7 - , QuickCheck >= 2.12.6.1 && < 2.14 + , QuickCheck >= 2.12.6.1 && < 2.15 , string-conversions >= 0.4.0.1 && < 0.5 , tagged >= 0.8.6 && < 0.9 , vault >= 0.3.1.2 && < 0.4 @@ -160,7 +160,7 @@ test-suite spec -- Additonal dependencies build-depends: hspec >= 2.6.0 && < 2.8 - , QuickCheck >= 2.12.6.1 && < 2.14 + , QuickCheck >= 2.12.6.1 && < 2.15 , quickcheck-instances >= 0.3.19 && < 0.4 build-tool-depends: From 6bd3ee80ca9a3ffdfc755bd139439b5e0eaab04f Mon Sep 17 00:00:00 2001 From: Taylor Fausak Date: Sun, 5 Apr 2020 12:15:30 -0400 Subject: [PATCH 011/156] Allow lens 4.19 https://github.com/ekmett/lens/blob/v4.19.1/CHANGELOG.markdown --- servant-foreign/servant-foreign.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 399e3870..c225976a 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -57,7 +57,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.12 - , lens >= 4.17 && < 4.19 + , lens >= 4.17 && < 4.20 , http-types >= 0.12.2 && < 0.13 hs-source-dirs: src From a221320f5aeec31189274c9803279d59920f1cd4 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sun, 26 Apr 2020 11:35:01 +0800 Subject: [PATCH 012/156] Allow base64-bytestring 1.1 Builds fine and all tests pass here. --- servant-server/servant-server.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 97fa7d2e..1ff4016a 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -82,7 +82,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.12 - , base64-bytestring >= 1.0.0.1 && < 1.1 + , base64-bytestring >= 1.0.0.1 && < 1.2 , exceptions >= 0.10.0 && < 0.11 , http-media >= 0.7.1.3 && < 0.9 , http-types >= 0.12.2 && < 0.13 From 85599b944cdeb9f05f1e2e659aa85bbcfb11cd89 Mon Sep 17 00:00:00 2001 From: David Johnson Date: Sat, 6 Jun 2020 00:04:32 -0400 Subject: [PATCH 013/156] Build servant repo with nix. (#1288) --- .gitignore | 4 ++++ default.nix | 38 ++++++++++++++++++++++++++++++++++++++ nix/nixpkgs.json | 4 ++++ 3 files changed, 46 insertions(+) create mode 100644 default.nix create mode 100644 nix/nixpkgs.json diff --git a/.gitignore b/.gitignore index baf818e7..a74ddee2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/*/dist +*~ dist-* .ghc.environment.* /bin @@ -30,5 +31,8 @@ doc/venv doc/tutorial/static/api.js doc/tutorial/static/jq.js +# nix +result* + # local versions of things servant-multipart diff --git a/default.nix b/default.nix new file mode 100644 index 00000000..e16129de --- /dev/null +++ b/default.nix @@ -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; + } + diff --git a/nix/nixpkgs.json b/nix/nixpkgs.json new file mode 100644 index 00000000..b6bf5f32 --- /dev/null +++ b/nix/nixpkgs.json @@ -0,0 +1,4 @@ +{ + "rev" : "05f0934825c2a0750d4888c4735f9420c906b388", + "sha256" : "1g8c2w0661qn89ajp44znmwfmghbbiygvdzq0rzlvlpdiz28v6gy" +} From 67cb564aeffff029995dbbfaa741100b71f9b340 Mon Sep 17 00:00:00 2001 From: Teymour Aldridge <42674621+teymour-aldridge@users.noreply.github.com> Date: Sat, 6 Jun 2020 05:30:16 +0100 Subject: [PATCH 014/156] Update README.md (#1300) Grammatical fix to documentation --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 54a0e423..2d239969 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ 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/). @@ -51,9 +51,9 @@ To regenerate the script use (*note:* atm you need to comment `doc/cookbook/` pa 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: +In case Travis jobs fail due to a dependency failing to build, you can temporarily +add `constraints` to the `cabal.project` file, and regenerate the `.travis.yml`. +For example, the following will disallow a single `troublemaker-13.37` package version: ``` constraints: @@ -62,7 +62,7 @@ constraints: ## 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) @@ -82,4 +82,4 @@ 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 From b9d8fbcdc122ca252fa0a136208c1a25457ec61f Mon Sep 17 00:00:00 2001 From: Jan Hrcek <2716069+jhrcek@users.noreply.github.com> Date: Sat, 6 Jun 2020 06:43:51 +0200 Subject: [PATCH 015/156] 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 --- CONTRIBUTING.md | 2 +- README.md | 4 ++-- doc/cookbook/basic-streaming/Streaming.lhs | 5 ++++- doc/cookbook/curl-mock/CurlMock.lhs | 2 +- doc/cookbook/db-mysql-basics/MysqlBasics.lhs | 2 +- doc/cookbook/file-upload/FileUpload.lhs | 4 ++-- doc/cookbook/generic/Generic.lhs | 6 +++--- .../HoistServerWithContext.lhs | 2 +- doc/cookbook/open-id-connect/OpenIdConnect.lhs | 14 +++++++------- doc/cookbook/pagination/Pagination.lhs | 16 ++++++++-------- doc/cookbook/sentry/Sentry.lhs | 2 +- .../structuring-apis/StructuringApis.lhs | 2 +- .../using-custom-monad/UsingCustomMonad.lhs | 2 +- .../using-free-client/UsingFreeClient.lhs | 2 +- doc/tutorial/Authentication.lhs | 12 ++++++------ doc/tutorial/Client.lhs | 4 ++-- doc/tutorial/Docs.lhs | 4 ++-- doc/tutorial/Javascript.lhs | 4 ++-- doc/tutorial/Server.lhs | 10 +++++----- servant-client-core/CHANGELOG.md | 2 +- servant-client-core/servant-client-core.cabal | 2 +- servant-client/CHANGELOG.md | 2 +- servant-client/servant-client.cabal | 2 +- .../Client/Internal/HttpClient/Streaming.hs | 2 +- servant-docs/example/greet.md | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-docs/src/Servant/Docs/Internal.hs | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-foreign/test/Servant/ForeignSpec.hs | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-http-streams/test/Servant/ClientSpec.hs | 6 +++--- servant-server/CHANGELOG.md | 4 ++-- servant-server/example/README.md | 2 +- servant-server/servant-server.cabal | 2 +- servant-server/src/Servant/Server/Internal.hs | 2 +- .../src/Servant/Server/Internal/BasicAuth.hs | 2 +- .../src/Servant/Server/Internal/Context.hs | 2 +- .../src/Servant/Server/Internal/Delayed.hs | 2 +- .../src/Servant/Server/Internal/DelayedIO.hs | 2 +- servant/CHANGELOG.md | 8 ++++---- servant/servant.cabal | 2 +- servant/src/Servant/API/ResponseHeaders.hs | 2 +- servant/src/Servant/Links.hs | 6 +++--- 43 files changed, 83 insertions(+), 80 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 46eb65f7..855e5a38 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -103,7 +103,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. diff --git a/README.md b/README.md index 2d239969..6310638c 100644 --- a/README.md +++ b/README.md @@ -32,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: @@ -60,7 +60,7 @@ constraints: troublemaker <13.37 && > 13.37 ``` -## TechEmpower framework bechmarks +## TechEmpower framework benchmarks We develop and maintain the servant TFB entry in https://github.com/haskell-servant/FrameworkBenchmarks/ diff --git a/doc/cookbook/basic-streaming/Streaming.lhs b/doc/cookbook/basic-streaming/Streaming.lhs index 69812a16..e027d8b8 100644 --- a/doc/cookbook/basic-streaming/Streaming.lhs +++ b/doc/cookbook/basic-streaming/Streaming.lhs @@ -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). diff --git a/doc/cookbook/curl-mock/CurlMock.lhs b/doc/cookbook/curl-mock/CurlMock.lhs index 57816c71..e573993d 100644 --- a/doc/cookbook/curl-mock/CurlMock.lhs +++ b/doc/cookbook/curl-mock/CurlMock.lhs @@ -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. diff --git a/doc/cookbook/db-mysql-basics/MysqlBasics.lhs b/doc/cookbook/db-mysql-basics/MysqlBasics.lhs index 01f0f884..1040ea8c 100644 --- a/doc/cookbook/db-mysql-basics/MysqlBasics.lhs +++ b/doc/cookbook/db-mysql-basics/MysqlBasics.lhs @@ -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. diff --git a/doc/cookbook/file-upload/FileUpload.lhs b/doc/cookbook/file-upload/FileUpload.lhs index 49c8cdd5..87a294d3 100644 --- a/doc/cookbook/file-upload/FileUpload.lhs +++ b/doc/cookbook/file-upload/FileUpload.lhs @@ -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 () diff --git a/doc/cookbook/generic/Generic.lhs b/doc/cookbook/generic/Generic.lhs index 29f33052..45180230 100644 --- a/doc/cookbook/generic/Generic.lhs +++ b/doc/cookbook/generic/Generic.lhs @@ -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 diff --git a/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs b/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs index ff6a2192..23d08462 100644 --- a/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs +++ b/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs @@ -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 diff --git a/doc/cookbook/open-id-connect/OpenIdConnect.lhs b/doc/cookbook/open-id-connect/OpenIdConnect.lhs index f94a9219..7db40280 100644 --- a/doc/cookbook/open-id-connect/OpenIdConnect.lhs +++ b/doc/cookbook/open-id-connect/OpenIdConnect.lhs @@ -8,7 +8,7 @@ some login token would be saved in the user agent local storage. Workflow: -1. user is presentend with a login button, +1. user is presented with a login button, 2. when the user click on the button it is redirected to the OIDC provider, 3. the user login in the OIDC provider, @@ -222,8 +222,8 @@ The `AuthInfo` is about the infos we can grab from OIDC provider. To be more precise, the user should come with a `code` (a token) and 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. +JWT containing all the information we need. Depending on the scopes we +asked we might get more information. ``` haskell -- | @AuthInfo@ @@ -248,13 +248,13 @@ 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. If there is no error query param but a `code` query param it means the user -sucessfully logged in. From there we need to make a request to the token +successfully logged in. From there we need to make a request to the token endpoint of the OIDC provider. Its a POST that should contains the code as well as the client id & secret. This is the role of the `requestTokens` to make this HTTP POST. @@ -332,7 +332,7 @@ data Customer = Customer { Here is the code that display the homepage. 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. +with some generated information. The page also display the content of the local storage. And in particular the items `api-key` and `user-id`. @@ -366,7 +366,7 @@ instance ToMarkup Homepage where We need some helpers to generate random string for generating state and API Keys. ``` 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 diff --git a/doc/cookbook/pagination/Pagination.lhs b/doc/cookbook/pagination/Pagination.lhs index 7c2c0e70..2bbb22ec 100644 --- a/doc/cookbook/pagination/Pagination.lhs +++ b/doc/cookbook/pagination/Pagination.lhs @@ -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 `` here can only be `name` and `` mus - `Range: [][; offset ][; limit ][; order ]` 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 diff --git a/doc/cookbook/sentry/Sentry.lhs b/doc/cookbook/sentry/Sentry.lhs index c0330cfd..ca221e7d 100644 --- a/doc/cookbook/sentry/Sentry.lhs +++ b/doc/cookbook/sentry/Sentry.lhs @@ -79,7 +79,7 @@ It does three things. First it initializes the service which will communicate wi - the Sentry `DSN`, which is obtained when creating a new project on Sentry - 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: diff --git a/doc/cookbook/structuring-apis/StructuringApis.lhs b/doc/cookbook/structuring-apis/StructuringApis.lhs index c59b01db..a8a82258 100644 --- a/doc/cookbook/structuring-apis/StructuringApis.lhs +++ b/doc/cookbook/structuring-apis/StructuringApis.lhs @@ -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 diff --git a/doc/cookbook/using-custom-monad/UsingCustomMonad.lhs b/doc/cookbook/using-custom-monad/UsingCustomMonad.lhs index baf1e095..e0336065 100644 --- a/doc/cookbook/using-custom-monad/UsingCustomMonad.lhs +++ b/doc/cookbook/using-custom-monad/UsingCustomMonad.lhs @@ -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: diff --git a/doc/cookbook/using-free-client/UsingFreeClient.lhs b/doc/cookbook/using-free-client/UsingFreeClient.lhs index d72ad6d5..0185c514 100644 --- a/doc/cookbook/using-free-client/UsingFreeClient.lhs +++ b/doc/cookbook/using-free-client/UsingFreeClient.lhs @@ -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 diff --git a/doc/tutorial/Authentication.lhs b/doc/tutorial/Authentication.lhs index da18b7ee..aa339d54 100644 --- a/doc/tutorial/Authentication.lhs +++ b/doc/tutorial/Authentication.lhs @@ -108,7 +108,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. @@ -260,7 +260,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 +274,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 +346,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,11 +385,11 @@ 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 -will be executed everytime a request matches a protected route. +will be executed every time a request matches a protected route. 4. Provide an instance of the `AuthServerData` type family, specifying your application-specific data type returned when authentication is successful (in our case this was `Account`). diff --git a/doc/tutorial/Client.lhs b/doc/tutorial/Client.lhs index 404ea027..35acee3d 100644 --- a/doc/tutorial/Client.lhs +++ b/doc/tutorial/Client.lhs @@ -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. diff --git a/doc/tutorial/Docs.lhs b/doc/tutorial/Docs.lhs index 91c93c71..047ce55f 100644 --- a/doc/tutorial/Docs.lhs +++ b/doc/tutorial/Docs.lhs @@ -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 diff --git a/doc/tutorial/Javascript.lhs b/doc/tutorial/Javascript.lhs index fbcd3c95..2ba6129f 100644 --- a/doc/tutorial/Javascript.lhs +++ b/doc/tutorial/Javascript.lhs @@ -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 diff --git a/doc/tutorial/Server.lhs b/doc/tutorial/Server.lhs index 4b3ff083..e7879172 100644 --- a/doc/tutorial/Server.lhs +++ b/doc/tutorial/Server.lhs @@ -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 @@ -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 diff --git a/servant-client-core/CHANGELOG.md b/servant-client-core/CHANGELOG.md index 9fa1ac36..b69c3235 100644 --- a/servant-client-core/CHANGELOG.md +++ b/servant-client-core/CHANGELOG.md @@ -205,7 +205,7 @@ - *servant-client-core* Add `hoistClient` to `HasClient`. 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. Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in). ([#936](https://github.com/haskell-servant/servant/pull/936)) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 3d1055db..d03da894 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -104,7 +104,7 @@ test-suite spec , base-compat , servant-client-core - -- Additonal dependencies + -- Additional dependencies build-depends: deepseq >= 1.4.2.0 && < 1.5 , hspec >= 2.6.0 && < 2.8 diff --git a/servant-client/CHANGELOG.md b/servant-client/CHANGELOG.md index 362ba567..adc6ef56 100644 --- a/servant-client/CHANGELOG.md +++ b/servant-client/CHANGELOG.md @@ -198,7 +198,7 @@ - *servant-client-core* Add `hoistClient` to `HasClient`. 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. Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in). ([#936](https://github.com/haskell-servant/servant/pull/936)) diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index bdbecc0c..2de0dd62 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -120,7 +120,7 @@ test-suite spec , wai , warp - -- Additonal dependencies + -- Additional dependencies build-depends: entropy >= 0.4.1.3 && < 0.5 , hspec >= 2.6.0 && < 2.8 diff --git a/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs b/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs index 2f5a1cb7..21658638 100644 --- a/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs +++ b/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs @@ -130,7 +130,7 @@ withClientM cm env k = -- streaming response types ('SourceT', 'Conduit', pipes 'Proxy' or 'Machine'). -- For those you have to use 'withClientM'. -- --- /Note:/ we 'force' the result, so the likehood of accidentally leaking a +-- /Note:/ we 'force' the result, so the likelihood of accidentally leaking a -- connection is smaller. Use with care. -- runClientM :: NFData a => ClientM a -> ClientEnv -> IO (Either ClientError a) diff --git a/servant-docs/example/greet.md b/servant-docs/example/greet.md index 322d3c28..fea5ce66 100644 --- a/servant-docs/example/greet.md +++ b/servant-docs/example/greet.md @@ -57,7 +57,7 @@ You'll also note that multiple intros are possible. This is some text -### Second secton +### Second section And some more diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index d5912394..ac08db15 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -105,7 +105,7 @@ test-suite spec , servant-docs , string-conversions - -- Additonal dependencies + -- Additional dependencies build-depends: tasty >= 1.1.0.4 && < 1.3, tasty-golden >= 2.3.2 && < 2.4, diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index 402fd427..b289e0c7 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -396,7 +396,7 @@ docsWithOptions p = docsFor p (defEndpoint, defAction) -- > extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete)) $ -- > defAction & headers <>~ ["unicorns"] -- > & notes <>~ [ DocNote "Title" ["This is some text"] --- > , DocNote "Second secton" ["And some more"] +-- > , DocNote "Second section" ["And some more"] -- > ] extraInfo :: (IsIn endpoint api, HasLink endpoint, HasDocs endpoint) diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index c225976a..5319511a 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -77,7 +77,7 @@ test-suite spec , servant , servant-foreign - -- Additonal dependencies + -- Additional dependencies build-depends: hspec >= 2.6.0 && <2.8 diff --git a/servant-foreign/test/Servant/ForeignSpec.hs b/servant-foreign/test/Servant/ForeignSpec.hs index 2bfe0555..76fb5351 100644 --- a/servant-foreign/test/Servant/ForeignSpec.hs +++ b/servant-foreign/test/Servant/ForeignSpec.hs @@ -114,7 +114,7 @@ listFromAPISpec = describe "listFromAPI" $ do shouldBe putReq $ defReq { _reqUrl = Url [ Segment $ Static "test" ] - -- Shoud this be |intX| or |listX of intX| ? + -- Should this be |intX| or |listX of intX| ? [ QueryArg (Arg "params" "listX of intX") List ] , _reqMethod = "PUT" , _reqHeaders = [] diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 3a9ee9c5..4975bf9d 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -114,7 +114,7 @@ test-suite spec , wai , warp - -- Additonal dependencies + -- Additional dependencies build-depends: entropy >= 0.4.1.3 && < 0.5 , hspec >= 2.6.0 && < 2.8 diff --git a/servant-http-streams/test/Servant/ClientSpec.hs b/servant-http-streams/test/Servant/ClientSpec.hs index aa0e0fb8..14efd88f 100644 --- a/servant-http-streams/test/Servant/ClientSpec.hs +++ b/servant-http-streams/test/Servant/ClientSpec.hs @@ -77,7 +77,7 @@ _ = client comprehensiveAPIWithoutStreaming spec :: Spec spec = describe "Servant.HttpStreams" $ do - sucessSpec + successSpec failSpec wrappedApiSpec basicAuthSpec @@ -262,8 +262,8 @@ runClientUnsafe x burl = withClientEnvIO burl (runClientMUnsafe x) where runClientMUnsafe x env = withClientM x env return -sucessSpec :: Spec -sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do +successSpec :: Spec +successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do it "Servant.API.Get root" $ \(_, baseUrl) -> do left show <$> runClient getRoot baseUrl `shouldReturn` Right carol diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index 436a9663..db6afd3e 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -35,7 +35,7 @@ Some APIs need query parameters rewriting, e.g. in order to support for multiple casing (camel, snake, etc) or something to that effect. - This could be easily achieved by using WAI Middleware and modyfing + This could be easily achieved by using WAI Middleware and modifying request's `Query`. But QueryParam, QueryParams and QueryFlag use `rawQueryString`. By using `queryString` rather then `rawQueryString` we can enable such rewritings. @@ -44,7 +44,7 @@ We used `build-type: Custom`, but it's problematic e.g. for cross-compiling. The benefit is small, as the doctests - can be run other ways too (though not so conviniently). + can be run other ways too (though not so conveniently). 0.16.2 ------ diff --git a/servant-server/example/README.md b/servant-server/example/README.md index a787d7c7..a50ff4cb 100644 --- a/servant-server/example/README.md +++ b/servant-server/example/README.md @@ -1,2 +1,2 @@ - `greet.hs` shows how to write a simple webservice, run it, query it with automatically-derived haskell functions and print the (generated) markdown documentation for the API. -- `greet.md` contains the aforementionned generated documentation. \ No newline at end of file +- `greet.md` contains the aforementioned generated documentation. \ No newline at end of file diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 1ff4016a..8b607ac4 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -155,7 +155,7 @@ test-suite spec , transformers-compat , wai - -- Additonal dependencies + -- Additional dependencies build-depends: aeson >= 1.4.1.0 && < 1.5 , directory >= 1.3.0.0 && < 1.4 diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index f1a24a19..b9597aa6 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -808,7 +808,7 @@ instance (HasContextEntry context (NamedContext name subContext), HasServer subA instance TypeError (HasServerArrowKindError arr) => HasServer ((arr :: k -> l) :> api) context where type ServerT (arr :> api) m = TypeError (HasServerArrowKindError arr) - -- it doens't really matter what sub route we peak + -- it doesn't really matter what sub route we peak route _ _ _ = error "servant-server panic: impossible happened in HasServer (arr :> api)" hoistServerWithContext _ _ _ = id diff --git a/servant-server/src/Servant/Server/Internal/BasicAuth.hs b/servant-server/src/Servant/Server/Internal/BasicAuth.hs index 8b5e06ae..b92e4b02 100644 --- a/servant-server/src/Servant/Server/Internal/BasicAuth.hs +++ b/servant-server/src/Servant/Server/Internal/BasicAuth.hs @@ -32,7 +32,7 @@ import Servant.Server.Internal.ServerError -- * Basic Auth -- | servant-server's current implementation of basic authentication is not --- immune to certian kinds of timing attacks. Decoding payloads does not take +-- immune to certain kinds of timing attacks. Decoding payloads does not take -- a fixed amount of time. -- | The result of authentication/authorization diff --git a/servant-server/src/Servant/Server/Internal/Context.hs b/servant-server/src/Servant/Server/Internal/Context.hs index 45a72761..9472cbba 100644 --- a/servant-server/src/Servant/Server/Internal/Context.hs +++ b/servant-server/src/Servant/Server/Internal/Context.hs @@ -20,7 +20,7 @@ import GHC.TypeLits -- -- If you are using combinators that require a non-empty 'Context' you have to -- use 'Servant.Server.serveWithContext' and pass it a 'Context' that contains all --- the values your combinators need. A 'Context' is essentially a heterogenous +-- the values your combinators need. A 'Context' is essentially a heterogeneous -- list and accessing the elements is being done by type (see 'getContextEntry'). -- The parameter of the type 'Context' is a type-level list reflecting the types -- of the contained context entries. To create a 'Context' with entries, use the diff --git a/servant-server/src/Servant/Server/Internal/Delayed.hs b/servant-server/src/Servant/Server/Internal/Delayed.hs index 1e580cf6..3ba89574 100644 --- a/servant-server/src/Servant/Server/Internal/Delayed.hs +++ b/servant-server/src/Servant/Server/Internal/Delayed.hs @@ -268,5 +268,5 @@ runAction action env req respond k = runResourceT $ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Due to GHC issue , we cannot -do the more succint thing - just update the records we actually change. +do the more succinct thing - just update the records we actually change. -} diff --git a/servant-server/src/Servant/Server/Internal/DelayedIO.hs b/servant-server/src/Servant/Server/Internal/DelayedIO.hs index 48f35b9d..6aaa23ae 100644 --- a/servant-server/src/Servant/Server/Internal/DelayedIO.hs +++ b/servant-server/src/Servant/Server/Internal/DelayedIO.hs @@ -24,7 +24,7 @@ import Servant.Server.Internal.ServerError -- | Computations used in a 'Delayed' can depend on the -- incoming 'Request', may perform 'IO', and result in a --- 'RouteResult', meaning they can either suceed, fail +-- 'RouteResult', meaning they can either succeed, fail -- (with the possibility to recover), or fail fatally. -- newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO)) a } diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index 6fecc9c1..dc3e783e 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -41,7 +41,7 @@ Some APIs need query parameters rewriting, e.g. in order to support for multiple casing (camel, snake, etc) or something to that effect. - This could be easily achieved by using WAI Middleware and modyfing + This could be easily achieved by using WAI Middleware and modifying request's `Query`. But QueryParam, QueryParams and QueryFlag use `rawQueryString`. By using `queryString` rather then `rawQueryString` we can enable such rewritings. @@ -50,7 +50,7 @@ We used `build-type: Custom`, but it's problematic e.g. for cross-compiling. The benefit is small, as the doctests - can be run other ways too (though not so conviniently). + can be run other ways too (though not so conveniently). - *servant* Remove deprecated modules [1268#](https://github.com/haskell-servant/servant/pull/1268) @@ -405,7 +405,7 @@ - *servant-client-core* Add `hoistClient` to `HasClient`. 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. Read [tutorial section for more information](https://docs.servant.dev/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in). ([#936](https://github.com/haskell-servant/servant/pull/936)) @@ -612,7 +612,7 @@ `enter` isn't exported from `Servant` module anymore. You can change `enter` to `hoistServer` in a straight forward way. - Unwrap natural transformation and add a api type `Proxy`: + Unwrap natural transformation and add an api type `Proxy`: ```diff -server = enter (NT nt) impl diff --git a/servant/servant.cabal b/servant/servant.cabal index 9425039e..a8098338 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -157,7 +157,7 @@ test-suite spec , text , transformers - -- Additonal dependencies + -- Additional dependencies build-depends: hspec >= 2.6.0 && < 2.8 , QuickCheck >= 2.12.6.1 && < 2.15 diff --git a/servant/src/Servant/API/ResponseHeaders.hs b/servant/src/Servant/API/ResponseHeaders.hs index 6ca42b6f..b5f98afd 100644 --- a/servant/src/Servant/API/ResponseHeaders.hs +++ b/servant/src/Servant/API/ResponseHeaders.hs @@ -95,7 +95,7 @@ type family HeaderValMap (f :: * -> *) (xs :: [*]) where class BuildHeadersTo hs where buildHeadersTo :: [HTTP.Header] -> HList hs - -- ^ Note: if there are multiple occurences of a header in the argument, + -- ^ Note: if there are multiple occurrences of a header in the argument, -- the values are interspersed with commas before deserialization (see -- ) diff --git a/servant/src/Servant/Links.hs b/servant/src/Servant/Links.hs index 0d07c201..b42738e2 100644 --- a/servant/src/Servant/Links.hs +++ b/servant/src/Servant/Links.hs @@ -54,7 +54,7 @@ -- >>> toUrlPiece $ safeLink api without -- "bye" -- --- If you would like create a helper for generating links only within that API, +-- If you would like to create a helper for generating links only within that API, -- you can partially apply safeLink if you specify a correct type signature -- like so: -- @@ -65,7 +65,7 @@ -- >>> apiLink = safeLink api -- >>> :} -- --- `safeLink'` allows to make specialise the output: +-- `safeLink'` allows you to specialise the output: -- -- >>> safeLink' toUrlPiece api without -- "bye" @@ -563,7 +563,7 @@ instance HasLink sub => HasLink (AuthProtect tag :> sub) where type MkLink (AuthProtect tag :> sub) a = MkLink sub a toLink = simpleToLink (Proxy :: Proxy sub) --- | Helper for implemneting 'toLink' for combinators not affecting link +-- | Helper for implementing 'toLink' for combinators not affecting link -- structure. simpleToLink :: forall sub a combinator. From 8e7b538921bf32bef2f83bf15e4c229dd317a975 Mon Sep 17 00:00:00 2001 From: Jan Hrcek <2716069+jhrcek@users.noreply.github.com> Date: Wed, 10 Jun 2020 18:36:23 +0200 Subject: [PATCH 016/156] More cookbook improvements (#1305) * Simplify code in CurlMock cookbook recipe * Link to latest versions of packages on hackage * Fix grammar in the OpenIdConnect recipe * HasForeignType -> HasForeign --- doc/cookbook/curl-mock/CurlMock.lhs | 26 +++++-------------- doc/cookbook/https/Https.lhs | 10 +++---- .../open-id-connect/OpenIdConnect.lhs | 18 ++++++------- doc/cookbook/sentry/Sentry.lhs | 2 +- doc/tutorial/Server.lhs | 4 +-- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/doc/cookbook/curl-mock/CurlMock.lhs b/doc/cookbook/curl-mock/CurlMock.lhs index e573993d..126349bf 100644 --- a/doc/cookbook/curl-mock/CurlMock.lhs +++ b/doc/cookbook/curl-mock/CurlMock.lhs @@ -86,7 +86,7 @@ listFromAPI :: (HasForeign lang ftype api, GenerateList ftype (Foreign ftype api ``` This looks a bit confusing... -[Here](https://hackage.haskell.org/package/servant-foreign-0.11.1/docs/Servant-Foreign.html#t:HasForeignType) is the documentation for the `HasForeign` typeclass. +[Here](https://hackage.haskell.org/package/servant-foreign/docs/Servant-Foreign.html#t:HasForeign) is the documentation for the `HasForeign` typeclass. We will not go into details here, but this allows us to create a value of type `ftype` for any type `a` in our API. In our case we want to create a mock of every type `a`. @@ -130,24 +130,12 @@ generateCurl :: (GenerateList Mocked (Foreign Mocked api), HasForeign NoLang Moc generateCurl p host = fmap T.unlines body where - body = foldr (\endp curlCalls -> mCons (generateEndpoint host endp) curlCalls) (return []) + body = mapM (generateEndpoint host) $ listFromAPI (Proxy :: Proxy NoLang) (Proxy :: Proxy Mocked) p ``` -To understand this function, better start at the end: - -`listFromAPI` gives us a list of endpoints. We iterate over them (`foldr`) and call `generateEndpoint` for every endpoint. - -As generate endpoint will not return `Text` but `IO Text` (remember we need some random bits to mock), we cannot just use the cons operator but need to build `IO [Text]` from `IO Text`s. - -``` haskell -mCons :: IO a -> IO [a] -> IO [a] -mCons ele list = - ele >>= \e -> list >>= \l -> return ( e : l ) -``` - - -Now comes the juicy part; accessing the endpoints data: +First, `listFromAPI` gives us a list of `Req Mocked`. Each `Req` describes one endpoint from the API type. +We generate a curl call for each of them using the following helper. ``` haskell generateEndpoint :: Text -> Req Mocked -> IO Text @@ -169,7 +157,7 @@ generateEndpoint host req = `servant-foreign` offers a multitude of lenses to be used with `Req`-values. `reqMethod` gives us a straigthforward `Network.HTTP.Types.Method`, `reqUrl` the url part and so on. -Just take a look at [the docs](https://hackage.haskell.org/package/servant-foreign-0.11.1/docs/Servant-Foreign.html). +Just take a look at [the docs](https://hackage.haskell.org/package/servant-foreign/docs/Servant-Foreign.html). But how do we get our mocked json string? This seems to be a bit to short to be true: @@ -201,7 +189,7 @@ And now, lets hook it all up in our main function: ``` haskell main :: IO () main = - generateCurl api "localhost:8081" >>= (\v -> T.IO.putStrLn v) + generateCurl api "localhost:8081" >>= T.IO.putStrLn ``` Done: @@ -213,6 +201,6 @@ curl -X POST -d '{"email":"wV򝣀_b򆎘:z񁊞򲙲!(3DM V","age":10,"name":"=|W"} ``` This is of course no complete curl call mock generator, many things including path arguments are missing. -But it correctly generate mock calls for simple POST requests. +But it correctly generates mock calls for simple POST requests. Also, we now know how to use `HasForeignType` and `listFromAPI` to generate anything we want. diff --git a/doc/cookbook/https/Https.lhs b/doc/cookbook/https/Https.lhs index de188463..6a95824d 100644 --- a/doc/cookbook/https/Https.lhs +++ b/doc/cookbook/https/Https.lhs @@ -34,16 +34,16 @@ app = serve api server ``` It's now time to actually run the `Application`. -The [`warp-tls`](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html) +The [`warp-tls`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html) package provides two functions for running an `Application`, called -[`runTLS`](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#v:runTLS) -and [`runTLSSocket`](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#v:runTLSSocket). +[`runTLS`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#v:runTLS) +and [`runTLSSocket`](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#v:runTLSSocket). We will be using the first one. It takes two arguments, -[the TLS settings](https://hackage.haskell.org/package/warp-tls-3.2.4/docs/Network-Wai-Handler-WarpTLS.html#t:TLSSettings) +[the TLS settings](https://hackage.haskell.org/package/warp-tls/docs/Network-Wai-Handler-WarpTLS.html#t:TLSSettings) (certificates, keys, ciphers, etc) -and [the warp settings](https://hackage.haskell.org/package/warp-3.2.12/docs/Network-Wai-Handler-Warp-Internal.html#t:Settings) +and [the warp settings](https://hackage.haskell.org/package/warp/docs/Network-Wai-Handler-Warp-Internal.html#t:Settings) (port, logger, etc). We will be using very simple settings for this example but you are of diff --git a/doc/cookbook/open-id-connect/OpenIdConnect.lhs b/doc/cookbook/open-id-connect/OpenIdConnect.lhs index 7db40280..6b4d0ca2 100644 --- a/doc/cookbook/open-id-connect/OpenIdConnect.lhs +++ b/doc/cookbook/open-id-connect/OpenIdConnect.lhs @@ -9,7 +9,7 @@ some login token would be saved in the user agent local storage. Workflow: 1. user is presented with a login button, -2. when the user click on the button it is redirected to the OIDC +2. when the user clicks on the button it is redirected to the OIDC provider, 3. the user login in the OIDC provider, 4. the OIDC provider will redirect the user and provide a `code`, @@ -221,7 +221,7 @@ The `AuthInfo` is about the infos we can grab from OIDC provider. To be more precise, the user should come with a `code` (a token) and POSTing that code to the correct OIDC provider endpoint should return a JSON -object. One of the field should be named `id_token` which should be a +object. One of the fields should be named `id_token` which should be a JWT containing all the information we need. Depending on the scopes we asked we might get more information. @@ -252,12 +252,12 @@ The `handleLoggedIn` is that part that will retrieve the information from the user once he is redirected from the OIDC Provider after login. If the user is redirected to the `redirect_uri` but with an `error` query -parameter then it means something goes wrong. +parameter then it means something went wrong. If there is no error query param but a `code` query param it means the user successfully logged in. From there we need to make a request to the token -endpoint of the OIDC provider. Its a POST that should contains the code -as well as the client id & secret. -This is the role of the `requestTokens` to make this HTTP POST. +endpoint of the OIDC provider. It's a POST that should contain the code +as well as the client id and secret. +Making this HTTP POST is the responsibility of `requestTokens`. From there we extract the `claims` of the JWT contained in one of the value of the JSON returned by the POST HTTP Request. @@ -329,12 +329,12 @@ data Customer = Customer { } ``` -Here is the code that display the homepage. +Here is the code that displays the homepage. It should contain a link to the the `/login` URL. -When the user will click on this link it will be redirected to Google login page +When the user clicks on this link it will be redirected to Google login page with some generated information. -The page also display the content of the local storage. +The page also displays the content of the local storage. And in particular the items `api-key` and `user-id`. Those items should be set after a successful login when the user is redirected to `/login/cb`. diff --git a/doc/cookbook/sentry/Sentry.lhs b/doc/cookbook/sentry/Sentry.lhs index ca221e7d..e2e41416 100644 --- a/doc/cookbook/sentry/Sentry.lhs +++ b/doc/cookbook/sentry/Sentry.lhs @@ -86,7 +86,7 @@ In the second step it actually sends our message to Sentry with the `register` f - the configured Sentry service which we just created - the name of the logger -- the error level (see [SentryLevel](https://hackage.haskell.org/package/raven-haskell-0.1.2.0/docs/System-Log-Raven-Types.html#t:SentryLevel) for the possible options) +- the error level (see [SentryLevel](https://hackage.haskell.org/package/raven-haskell/docs/System-Log-Raven-Types.html#t:SentryLevel) for the possible options) - the message we want to send - an update function to handle the specific `SentryRecord` diff --git a/doc/tutorial/Server.lhs b/doc/tutorial/Server.lhs index e7879172..61769a30 100644 --- a/doc/tutorial/Server.lhs +++ b/doc/tutorial/Server.lhs @@ -620,7 +620,7 @@ In short, this means that a handler of type `Handler a` is simply equivalent to a computation of type `IO (Either ServerError a)`, that is, an IO action that either returns an error or a result. -The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Except.html#t:ExceptT) +The module [`Control.Monad.Except`](https://hackage.haskell.org/package/mtl/docs/Control-Monad-Except.html#t:ExceptT) from which `ExceptT` comes is worth looking at. Perhaps most importantly, `ExceptT` and `Handler` are instances of `MonadError`, so `throwError` can be used to return an error from your handler (whereas `return` @@ -634,7 +634,7 @@ kind and abort early. The next two sections cover how to do just that. Other important instances from the list above are `MonadIO m => MonadIO (ExceptT e m)`, and therefore also `MonadIO Handler` as there is a `MonadIO IO` instance. -[`MonadIO`](http://hackage.haskell.org/package/transformers-0.4.3.0/docs/Control-Monad-IO-Class.html) +[`MonadIO`](http://hackage.haskell.org/package/base/docs/Control-Monad-IO-Class.html#t:MonadIO) is a class from the **transformers** package defined as: ``` haskell ignore From c778a1837293b4809157e737e022e11385c08f24 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Fri, 12 Jun 2020 14:38:09 +0800 Subject: [PATCH 017/156] Allow aeson 1.5 (#1302) Builds fine and all tests pass here. --- servant/servant.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant/servant.cabal b/servant/servant.cabal index a8098338..2d42c310 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -96,7 +96,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.12 - , aeson >= 1.4.1.0 && < 1.5 + , aeson >= 1.4.1.0 && < 1.6 , attoparsec >= 0.13.2.2 && < 0.14 , bifunctors >= 5.5.3 && < 5.6 , case-insensitive >= 1.2.0.11 && < 1.3 From 4b225c23d78b8af3f821a8f9ec3060a17388391b Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sat, 13 Jun 2020 03:02:39 +0800 Subject: [PATCH 018/156] Allow aeson 1.5 in all components (#1309) --- servant-client-core/servant-client-core.cabal | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-server/servant-server.cabal | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index d03da894..4d4b715a 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -74,7 +74,7 @@ library -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. build-depends: - aeson >= 1.4.1.0 && < 1.5 + aeson >= 1.4.1.0 && < 1.6 , base-compat >= 0.10.5 && < 0.12 , base64-bytestring >= 1.0.0.1 && < 1.1 , exceptions >= 0.10.0 && < 0.11 diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index ac08db15..34fc915f 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -57,7 +57,7 @@ library -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. build-depends: - aeson >= 1.4.1.0 && < 1.5 + aeson >= 1.4.1.0 && < 1.6 , aeson-pretty >= 0.8.5 && < 0.9 , base-compat >= 0.10.5 && < 0.12 , case-insensitive >= 1.2.0.11 && < 1.3 diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 8b607ac4..26c017d9 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -115,7 +115,7 @@ executable greet , text build-depends: - aeson >= 1.4.1.0 && < 1.5 + aeson >= 1.4.1.0 && < 1.6 , warp >= 3.2.25 && < 3.4 test-suite spec @@ -157,7 +157,7 @@ test-suite spec -- Additional dependencies build-depends: - aeson >= 1.4.1.0 && < 1.5 + aeson >= 1.4.1.0 && < 1.6 , directory >= 1.3.0.0 && < 1.4 , hspec >= 2.6.0 && < 2.8 , hspec-wai >= 0.10.1 && < 0.11 From a8184a2ee0c9987e618141c0c52f9110b927ea0b Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Sat, 13 Jun 2020 15:10:07 +0100 Subject: [PATCH 019/156] Add KnownStatus typeclass --- servant/servant.cabal | 1 + servant/src/Servant/API/Status.hs | 153 ++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 servant/src/Servant/API/Status.hs diff --git a/servant/servant.cabal b/servant/servant.cabal index 2d42c310..bf540e25 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -55,6 +55,7 @@ library Servant.API.RemoteHost Servant.API.ReqBody Servant.API.ResponseHeaders + Servant.API.Status Servant.API.Stream Servant.API.Sub Servant.API.TypeLevel diff --git a/servant/src/Servant/API/Status.hs b/servant/src/Servant/API/Status.hs new file mode 100644 index 00000000..8f80938d --- /dev/null +++ b/servant/src/Servant/API/Status.hs @@ -0,0 +1,153 @@ +{-# LANGUAGE DataKinds #-} +module Servant.API.Status where + +import Network.HTTP.Types.Status +import GHC.TypeNats + +-- | Witness that a type-level natural number corresponds to a HTTP status code +class KnownNat n => KnownStatus n where + statusVal :: proxy n -> Status + +instance KnownStatus 100 where + statusVal _ = status100 + +instance KnownStatus 101 where + statusVal _ = status101 + +instance KnownStatus 200 where + statusVal _ = status200 + +instance KnownStatus 201 where + statusVal _ = status201 + +instance KnownStatus 202 where + statusVal _ = status202 + +instance KnownStatus 203 where + statusVal _ = status203 + +instance KnownStatus 204 where + statusVal _ = status204 + +instance KnownStatus 205 where + statusVal _ = status205 + +instance KnownStatus 206 where + statusVal _ = status206 + +instance KnownStatus 300 where + statusVal _ = status300 + +instance KnownStatus 301 where + statusVal _ = status301 + +instance KnownStatus 302 where + statusVal _ = status302 + +instance KnownStatus 303 where + statusVal _ = status303 + +instance KnownStatus 304 where + statusVal _ = status304 + +instance KnownStatus 305 where + statusVal _ = status305 + +instance KnownStatus 307 where + statusVal _ = status307 + +instance KnownStatus 308 where + statusVal _ = status308 + +instance KnownStatus 400 where + statusVal _ = status400 + +instance KnownStatus 401 where + statusVal _ = status401 + +instance KnownStatus 402 where + statusVal _ = status402 + +instance KnownStatus 403 where + statusVal _ = status403 + +instance KnownStatus 404 where + statusVal _ = status404 + +instance KnownStatus 405 where + statusVal _ = status405 + +instance KnownStatus 406 where + statusVal _ = status406 + +instance KnownStatus 407 where + statusVal _ = status407 + +instance KnownStatus 408 where + statusVal _ = status408 + +instance KnownStatus 409 where + statusVal _ = status409 + +instance KnownStatus 410 where + statusVal _ = status410 + +instance KnownStatus 411 where + statusVal _ = status411 + +instance KnownStatus 412 where + statusVal _ = status412 + +instance KnownStatus 413 where + statusVal _ = status413 + +instance KnownStatus 414 where + statusVal _ = status414 + +instance KnownStatus 415 where + statusVal _ = status415 + +instance KnownStatus 416 where + statusVal _ = status416 + +instance KnownStatus 417 where + statusVal _ = status417 + +instance KnownStatus 418 where + statusVal _ = status418 + +instance KnownStatus 422 where + statusVal _ = status422 + +instance KnownStatus 426 where + statusVal _ = status426 + +instance KnownStatus 428 where + statusVal _ = status428 + +instance KnownStatus 429 where + statusVal _ = status429 + +instance KnownStatus 431 where + statusVal _ = status431 + +instance KnownStatus 500 where + statusVal _ = status500 + +instance KnownStatus 501 where + statusVal _ = status501 + +instance KnownStatus 502 where + statusVal _ = status502 + +instance KnownStatus 503 where + statusVal _ = status503 + +instance KnownStatus 504 where + statusVal _ = status504 + +instance KnownStatus 505 where + statusVal _ = status505 + +instance KnownStatus 511 where + statusVal _ = status511 From 6889d053c7139a08c57a4fb1b4888fec17c6f1ed Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Sat, 13 Jun 2020 15:38:36 +0100 Subject: [PATCH 020/156] Add FlexibleInstances for earlier GHCs --- servant/src/Servant/API/Status.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/servant/src/Servant/API/Status.hs b/servant/src/Servant/API/Status.hs index 8f80938d..17a60766 100644 --- a/servant/src/Servant/API/Status.hs +++ b/servant/src/Servant/API/Status.hs @@ -1,4 +1,6 @@ {-# LANGUAGE DataKinds #-} +-- Flexible instances is necessary on GHC 8.4 and earlier +{-# LANGUAGE FlexibleInstances #-} module Servant.API.Status where import Network.HTTP.Types.Status From ff9da1cde499d41fe20fcf167b8c3cee87278979 Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Sat, 13 Jun 2020 15:50:12 +0100 Subject: [PATCH 021/156] Use GHC.TypeLits rather than TypeNats --- servant/src/Servant/API/Status.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant/src/Servant/API/Status.hs b/servant/src/Servant/API/Status.hs index 17a60766..ee334fcd 100644 --- a/servant/src/Servant/API/Status.hs +++ b/servant/src/Servant/API/Status.hs @@ -4,7 +4,7 @@ module Servant.API.Status where import Network.HTTP.Types.Status -import GHC.TypeNats +import GHC.TypeLits -- | Witness that a type-level natural number corresponds to a HTTP status code class KnownNat n => KnownStatus n where From 0530671ad62fb40690baeed83b03ca0e09f1f7f7 Mon Sep 17 00:00:00 2001 From: Leif Warner Date: Wed, 6 May 2020 16:15:07 -0700 Subject: [PATCH 022/156] Allow newer versions of base, template-haskell, lens, & unliftio-core --- servant-client-core/servant-client-core.cabal | 4 ++-- servant-client/servant-client.cabal | 2 +- servant-conduit/servant-conduit.cabal | 2 +- servant-docs/servant-docs.cabal | 4 ++-- servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 4d4b715a..a3b44dca 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -55,13 +55,13 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 , text >= 1.2.3.0 && < 1.3 , transformers >= 0.5.2.0 && < 0.6 - , template-haskell >= 2.11.1.0 && < 2.16 + , template-haskell >= 2.11.1.0 && < 2.17 if !impl(ghc >= 8.2) build-depends: diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 2de0dd62..d920577b 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -45,7 +45,7 @@ library -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 154af5db..df07ace7 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -40,7 +40,7 @@ library , mtl >=2.2.2 && <2.3 , resourcet >=1.2.2 && <1.3 , servant >=0.15 && <0.18 - , unliftio-core >=0.1.2.0 && <0.2 + , unliftio-core >=0.1.2.0 && <0.3 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 34fc915f..65c5be14 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -46,7 +46,7 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , text >= 1.2.3.0 && < 1.3 @@ -64,7 +64,7 @@ library , hashable >= 1.2.7.0 && < 1.4 , http-media >= 0.7.1.3 && < 0.9 , http-types >= 0.12.2 && < 0.13 - , lens >= 4.17 && < 4.19 + , lens >= 4.17 && < 4.20 , string-conversions >= 0.4.0.1 && < 0.5 , universe-base >= 1.1.1 && < 1.2 , unordered-containers >= 0.2.9.0 && < 0.3 diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 5319511a..5ab34718 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -46,7 +46,7 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , text >= 1.2.3.0 && < 1.3 -- Servant dependencies diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 4975bf9d..f21dcc73 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -43,7 +43,7 @@ library -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 26c017d9..5b0407df 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -64,7 +64,7 @@ library -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , containers >= 0.5.7.1 && < 0.7 , mtl >= 2.2.2 && < 2.3 diff --git a/servant/servant.cabal b/servant/servant.cabal index bf540e25..b0c92303 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -80,7 +80,7 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.14 + base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , mtl >= 2.2.2 && < 2.3 , transformers >= 0.5.2.0 && < 0.6 From 7ddc2e7b9e4587f36cba824dcb73ed77452c7eac Mon Sep 17 00:00:00 2001 From: Leif Warner Date: Wed, 6 May 2020 16:31:00 -0700 Subject: [PATCH 023/156] Add GHC 8.10.1 to .travis.yml build. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1341b9e4..8c73b5e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,9 @@ jobs: - 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.10.1 + 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.10.1","cabal-install-3.2"]}} + 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 From e3c4f5d85e5647e2a0c8de7716c4c703e9866dc7 Mon Sep 17 00:00:00 2001 From: Leif Warner Date: Wed, 6 May 2020 16:38:36 -0700 Subject: [PATCH 024/156] Bump doctest version used for ghc 8.10.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8c73b5e9..1ef279d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,7 +110,7 @@ install: - 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 $WITHCOMPILER -j2 doctest --constraint='doctest ==0.16.3.*') ; 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 From eaadc9ec1f666ffd3c8ebba1a348650ff805ab28 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Mon, 29 Jun 2020 21:53:03 +0200 Subject: [PATCH 025/156] Relax upper bound for aeson. --- servant-docs/servant-docs.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 65c5be14..b54c9c3e 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -111,4 +111,3 @@ test-suite spec tasty-golden >= 2.3.2 && < 2.4, tasty-hunit >= 0.10.0.1 && < 0.11, transformers >= 0.5.2.0 && < 0.6 - From 57f0b0b390cc2e799f46622950a349d7bff18ef0 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sun, 14 Jun 2020 12:15:30 +0300 Subject: [PATCH 026/156] 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 --- servant-server/servant-server.cabal | 3 +- servant-server/src/Servant/Server.hs | 32 ++++- servant-server/src/Servant/Server/Generic.hs | 1 + servant-server/src/Servant/Server/Internal.hs | 114 +++++++++++------- .../src/Servant/Server/Internal/Context.hs | 31 +++-- .../Servant/Server/Internal/ErrorFormatter.hs | 79 ++++++++++++ .../src/Servant/Server/Internal/Router.hs | 37 +++--- .../test/Servant/Server/RouterSpec.hs | 4 +- servant/src/Servant/API/Header.hs | 2 +- 9 files changed, 228 insertions(+), 75 deletions(-) create mode 100644 servant-server/src/Servant/Server/Internal/ErrorFormatter.hs diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 5b0407df..374220ef 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -50,9 +50,10 @@ library Servant.Server.Internal.Context Servant.Server.Internal.Delayed Servant.Server.Internal.DelayedIO + Servant.Server.Internal.ErrorFormatter Servant.Server.Internal.Handler - Servant.Server.Internal.Router Servant.Server.Internal.RouteResult + Servant.Server.Internal.Router Servant.Server.Internal.RoutingApplication Servant.Server.Internal.ServerError Servant.Server.StaticFiles diff --git a/servant-server/src/Servant/Server.hs b/servant-server/src/Servant/Server.hs index e2d9f3c5..99c0b1e5 100644 --- a/servant-server/src/Servant/Server.hs +++ b/servant-server/src/Servant/Server.hs @@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} -- | This module lets you implement 'Server's for defined APIs. You'll -- most likely just need 'serve'. @@ -35,6 +36,8 @@ module Servant.Server -- * Context , Context(..) , HasContextEntry(getContextEntry) + , type (.++) + , (.++) -- ** NamedContext , NamedContext(..) , descendIntoNamedContext @@ -86,6 +89,24 @@ module Servant.Server , err504 , err505 + -- * Formatting of errors from combinators + -- + -- | You can configure how Servant will render errors that occur while parsing the request. + + , ErrorFormatter + , NotFoundErrorFormatter + , ErrorFormatters + + , bodyParserErrorFormatter + , urlParseErrorFormatter + , headerParseErrorFormatter + , notFoundErrorFormatter + + , DefaultErrorFormatters + , defaultErrorFormatters + + , getAcceptHeader + -- * Re-exports , Application , Tagged (..) @@ -129,10 +150,17 @@ import Servant.Server.Internal serve :: (HasServer api '[]) => Proxy api -> Server api -> Application serve p = serveWithContext p EmptyContext -serveWithContext :: (HasServer api context) +-- | Like 'serve', but allows you to pass custom context. +-- +-- 'defaultErrorFormatters' will always be appended to the end of the passed context, +-- but if you pass your own formatter, it will override the default one. +serveWithContext :: ( HasServer api context + , HasContextEntry (context .++ DefaultErrorFormatters) ErrorFormatters ) => Proxy api -> Context context -> Server api -> Application serveWithContext p context server = - toApplication (runRouter (route p context (emptyDelayed (Route server)))) + toApplication (runRouter format404 (route p context (emptyDelayed (Route server)))) + where + format404 = notFoundErrorFormatter . getContextEntry . mkContextWithErrorFormatter $ context -- | Hoist server implementation. -- diff --git a/servant-server/src/Servant/Server/Generic.hs b/servant-server/src/Servant/Server/Generic.hs index 88dbe331..c3db01c3 100644 --- a/servant-server/src/Servant/Server/Generic.hs +++ b/servant-server/src/Servant/Server/Generic.hs @@ -67,6 +67,7 @@ genericServeTWithContext ( GenericServant routes (AsServerT m) , GenericServant routes AsApi , HasServer (ToServantApi routes) ctx + , HasContextEntry (ctx .++ DefaultErrorFormatters) ErrorFormatters , ServerT (ToServantApi routes) m ~ ToServant routes (AsServerT m) ) => (forall a. m a -> Handler a) -- ^ 'hoistServer' argument to come back to 'Handler' diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index b9597aa6..9fa0187b 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -24,6 +24,7 @@ module Servant.Server.Internal , module Servant.Server.Internal.Context , module Servant.Server.Internal.Delayed , module Servant.Server.Internal.DelayedIO + , module Servant.Server.Internal.ErrorFormatter , module Servant.Server.Internal.Handler , module Servant.Server.Internal.Router , module Servant.Server.Internal.RouteResult @@ -95,6 +96,7 @@ import Servant.Server.Internal.BasicAuth import Servant.Server.Internal.Context import Servant.Server.Internal.Delayed import Servant.Server.Internal.DelayedIO +import Servant.Server.Internal.ErrorFormatter import Servant.Server.Internal.Handler import Servant.Server.Internal.Router import Servant.Server.Internal.RouteResult @@ -168,7 +170,10 @@ instance (HasServer a context, HasServer b context) => HasServer (a :<|> b) cont -- > server = getBook -- > where getBook :: Text -> Handler Book -- > getBook isbn = ... -instance (KnownSymbol capture, FromHttpApiData a, HasServer api context, SBoolI (FoldLenient mods)) +instance (KnownSymbol capture, FromHttpApiData a + , HasServer api context, SBoolI (FoldLenient mods) + , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters + ) => HasServer (Capture' mods capture a :> api) context where type ServerT (Capture' mods capture a :> api) m = @@ -180,12 +185,15 @@ instance (KnownSymbol capture, FromHttpApiData a, HasServer api context, SBoolI CaptureRouter $ route (Proxy :: Proxy api) context - (addCapture d $ \ txt -> case ( sbool :: SBool (FoldLenient mods) - , parseUrlPiece txt :: Either T.Text a) of - (SFalse, Left e) -> delayedFail err400 { errBody = cs e } - (SFalse, Right v) -> return v - (STrue, piece) -> return $ (either (Left . cs) Right) piece - ) + (addCapture d $ \ txt -> withRequest $ \ request -> + case ( sbool :: SBool (FoldLenient mods) + , parseUrlPiece txt :: Either T.Text a) of + (SFalse, Left e) -> delayedFail $ formatError rep request $ cs e + (SFalse, Right v) -> return v + (STrue, piece) -> return $ (either (Left . cs) Right) piece) + where + rep = typeRep (Proxy :: Proxy Capture') + formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context) -- | If you use 'CaptureAll' in one of the endpoints for your API, -- this automatically requires your server-side handler to be a @@ -204,7 +212,10 @@ instance (KnownSymbol capture, FromHttpApiData a, HasServer api context, SBoolI -- > server = getSourceFile -- > where getSourceFile :: [Text] -> Handler Book -- > getSourceFile pathSegments = ... -instance (KnownSymbol capture, FromHttpApiData a, HasServer api context) +instance (KnownSymbol capture, FromHttpApiData a + , HasServer api context + , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters + ) => HasServer (CaptureAll capture a :> api) context where type ServerT (CaptureAll capture a :> api) m = @@ -216,11 +227,14 @@ instance (KnownSymbol capture, FromHttpApiData a, HasServer api context) CaptureAllRouter $ route (Proxy :: Proxy api) context - (addCapture d $ \ txts -> case parseUrlPieces txts of - Left _ -> delayedFail err400 - Right v -> return v + (addCapture d $ \ txts -> withRequest $ \ request -> + case parseUrlPieces txts of + Left e -> delayedFail $ formatError rep request $ cs e + Right v -> return v ) - + where + rep = typeRep (Proxy :: Proxy CaptureAll) + formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context) allowedMethodHead :: Method -> Request -> Bool allowedMethodHead method request = method == methodGet && requestMethod request == methodHead @@ -240,10 +254,10 @@ methodCheck method request -- body check is no longer an option. However, we now run the accept -- check before the body check and can therefore afford to make it -- recoverable. -acceptCheck :: (AllMime list) => Proxy list -> B.ByteString -> DelayedIO () +acceptCheck :: (AllMime list) => Proxy list -> AcceptHeader -> DelayedIO () acceptCheck proxy accH - | canHandleAcceptH proxy (AcceptHeader accH) = return () - | otherwise = delayedFail err406 + | canHandleAcceptH proxy accH = return () + | otherwise = delayedFail err406 methodRouter :: (AllCTRender ctypes a) => (b -> ([(HeaderName, B.ByteString)], a)) @@ -253,12 +267,12 @@ methodRouter :: (AllCTRender ctypes a) methodRouter splitHeaders method proxy status action = leafRouter route' where route' env request respond = - let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request + let accH = getAcceptHeader request in runAction (action `addMethodCheck` methodCheck method request `addAcceptCheck` acceptCheck proxy accH ) env request respond $ \ output -> do let (headers, b) = splitHeaders output - case handleAcceptH proxy (AcceptHeader accH) b of + case handleAcceptH proxy accH b of Nothing -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does Just (contentT, body) -> let bdy = if allowedMethodHead method request then "" else body @@ -343,7 +357,7 @@ streamRouter :: forall ctype a c chunk env framing. (MimeRender ctype chunk, Fra -> Delayed env (Handler c) -> Router env streamRouter splitHeaders method status framingproxy ctypeproxy action = leafRouter $ \env request respond -> - let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request + let AcceptHeader accH = getAcceptHeader request cmediatype = NHM.matchAccept [contentType ctypeproxy] accH accCheck = when (isNothing cmediatype) $ delayedFail err406 contentHeader = (hContentType, NHM.renderHeader . maybeToList $ cmediatype) @@ -388,6 +402,7 @@ streamRouter splitHeaders method status framingproxy ctypeproxy action = leafRou instance (KnownSymbol sym, FromHttpApiData a, HasServer api context , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods) + , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters ) => HasServer (Header' mods sym a :> api) context where ------ @@ -399,6 +414,9 @@ instance route Proxy context subserver = route (Proxy :: Proxy api) context $ subserver `addHeaderCheck` withRequest headerCheck where + rep = typeRep (Proxy :: Proxy Header') + formatError = headerParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context) + headerName :: IsString n => n headerName = fromString $ symbolVal (Proxy :: Proxy sym) @@ -409,15 +427,13 @@ instance mev :: Maybe (Either T.Text a) mev = fmap parseHeader $ lookup headerName (requestHeaders req) - errReq = delayedFailFatal err400 - { errBody = "Header " <> headerName <> " is required" - } + errReq = delayedFailFatal $ formatError rep req + $ "Header " <> headerName <> " is required" - errSt e = delayedFailFatal err400 - { errBody = cs $ "Error parsing header " - <> headerName - <> " failed: " <> e - } + errSt e = delayedFailFatal $ formatError rep req + $ cs $ "Error parsing header " + <> headerName + <> " failed: " <> e -- | If you use @'QueryParam' "author" Text@ in one of the endpoints for your API, -- this automatically requires your server-side handler to be a function @@ -443,6 +459,7 @@ instance instance ( KnownSymbol sym, FromHttpApiData a, HasServer api context , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods) + , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters ) => HasServer (QueryParam' mods sym a :> api) context where ------ @@ -455,6 +472,9 @@ instance let querytext = queryToQueryText . queryString paramname = cs $ symbolVal (Proxy :: Proxy sym) + rep = typeRep (Proxy :: Proxy QueryParam') + formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context) + parseParam :: Request -> DelayedIO (RequestArgument mods a) parseParam req = unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev @@ -462,14 +482,12 @@ instance mev :: Maybe (Either T.Text a) mev = fmap parseQueryParam $ join $ lookup paramname $ querytext req - errReq = delayedFailFatal err400 - { errBody = cs $ "Query parameter " <> paramname <> " is required" - } + errReq = delayedFailFatal $ formatError rep req + $ cs $ "Query parameter " <> paramname <> " is required" - errSt e = delayedFailFatal err400 - { errBody = cs $ "Error parsing query parameter " - <> paramname <> " failed: " <> e - } + errSt e = delayedFailFatal $ formatError rep req + $ cs $ "Error parsing query parameter " + <> paramname <> " failed: " <> e delayed = addParameterCheck subserver . withRequest $ \req -> parseParam req @@ -495,7 +513,8 @@ instance -- > server = getBooksBy -- > where getBooksBy :: [Text] -> Handler [Book] -- > getBooksBy authors = ...return all books by these authors... -instance (KnownSymbol sym, FromHttpApiData a, HasServer api context) +instance (KnownSymbol sym, FromHttpApiData a, HasServer api context + , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters) => HasServer (QueryParams sym a :> api) context where type ServerT (QueryParams sym a :> api) m = @@ -506,21 +525,23 @@ instance (KnownSymbol sym, FromHttpApiData a, HasServer api context) route Proxy context subserver = route (Proxy :: Proxy api) context $ subserver `addParameterCheck` withRequest paramsCheck where + rep = typeRep (Proxy :: Proxy QueryParams) + formatError = urlParseErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context) + paramname = cs $ symbolVal (Proxy :: Proxy sym) paramsCheck req = case partitionEithers $ fmap parseQueryParam params of ([], parsed) -> return parsed - (errs, _) -> delayedFailFatal err400 - { errBody = cs $ "Error parsing query parameter(s) " - <> paramname <> " failed: " - <> T.intercalate ", " errs - } + (errs, _) -> delayedFailFatal $ formatError rep req + $ cs $ "Error parsing query parameter(s) " + <> paramname <> " failed: " + <> T.intercalate ", " errs where params :: [T.Text] params = mapMaybe snd . filter (looksLikeParam . fst) - . queryToQueryText - . queryString + . queryToQueryText + . queryString $ req looksLikeParam name = name == paramname || name == (paramname <> "[]") @@ -588,7 +609,7 @@ instance HasServer Raw context where -- The @Content-Type@ header is inspected, and the list provided is used to -- attempt deserialization. If the request does not have a @Content-Type@ -- header, it is treated as @application/octet-stream@ (as specified in --- . +-- [RFC 7231 section 3.1.1.5](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)). -- This lets servant worry about extracting it from the request and turning -- it into a value of the type you specify. -- @@ -604,6 +625,7 @@ instance HasServer Raw context where -- > where postBook :: Book -> Handler Book -- > postBook book = ...insert into your db... instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods) + , HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters ) => HasServer (ReqBody' mods list a :> api) context where type ServerT (ReqBody' mods list a :> api) m = @@ -615,6 +637,9 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods = route (Proxy :: Proxy api) context $ addBodyCheck subserver ctCheck bodyCheck where + rep = typeRep (Proxy :: Proxy ReqBody') + formatError = bodyParserErrorFormatter $ getContextEntry (mkContextWithErrorFormatter context) + -- Content-Type check, we only lookup we can try to parse the request body ctCheck = withRequest $ \ request -> do -- See HTTP RFC 2616, section 7.2.1 @@ -633,7 +658,7 @@ instance ( AllCTUnrender list a, HasServer api context, SBoolI (FoldLenient mods case sbool :: SBool (FoldLenient mods) of STrue -> return mrqbody SFalse -> case mrqbody of - Left e -> delayedFailFatal err400 { errBody = cs e } + Left e -> delayedFailFatal $ formatError rep request e Right v -> return v instance @@ -761,6 +786,9 @@ instance ( KnownSymbol realm ct_wildcard :: B.ByteString ct_wildcard = "*" <> "/" <> "*" -- Because CPP +getAcceptHeader :: Request -> AcceptHeader +getAcceptHeader = AcceptHeader . fromMaybe ct_wildcard . lookup hAccept . requestHeaders + -- * General Authentication diff --git a/servant-server/src/Servant/Server/Internal/Context.hs b/servant-server/src/Servant/Server/Internal/Context.hs index 9472cbba..cb4c23be 100644 --- a/servant-server/src/Servant/Server/Internal/Context.hs +++ b/servant-server/src/Servant/Server/Internal/Context.hs @@ -1,11 +1,12 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} module Servant.Server.Internal.Context where @@ -45,6 +46,20 @@ instance Eq (Context '[]) where instance (Eq a, Eq (Context as)) => Eq (Context (a ': as)) where x1 :. y1 == x2 :. y2 = x1 == x2 && y1 == y2 +-- | Append two type-level lists. +-- +-- Hint: import it as +-- +-- > import Servant.Server (type (.++)) +type family (.++) (l1 :: [*]) (l2 :: [*]) where + '[] .++ a = a + (a ': as) .++ b = a ': (as .++ b) + +-- | Append two contexts. +(.++) :: Context l1 -> Context l2 -> Context (l1 .++ l2) +EmptyContext .++ a = a +(a :. as) .++ b = a :. (as .++ b) + -- | This class is used to access context entries in 'Context's. 'getContextEntry' -- returns the first value where the type matches: -- diff --git a/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs b/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs new file mode 100644 index 00000000..c5a7b221 --- /dev/null +++ b/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs @@ -0,0 +1,79 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeOperators #-} + +module Servant.Server.Internal.ErrorFormatter + where + +import Data.String.Conversions + (cs) +import Data.Typeable +import Network.Wai.Internal + (Request) + +import Servant.API + (Capture, ReqBody) +import Servant.Server.Internal.Context +import Servant.Server.Internal.ServerError + +-- | 'Context' that contains default error formatters. +type DefaultErrorFormatters = '[ErrorFormatters] + +-- | A collection of error formatters for different situations. +-- +-- If you need to override one of them, use 'defaultErrorFormatters' with record update syntax. +data ErrorFormatters = ErrorFormatters + { -- | Format error from parsing the request body. + bodyParserErrorFormatter :: ErrorFormatter + -- | Format error from parsing url parts or query parameters. + , urlParseErrorFormatter :: ErrorFormatter + -- | Format error from parsing request headers. + , headerParseErrorFormatter :: ErrorFormatter + -- | Format error for not found URLs. + , notFoundErrorFormatter :: NotFoundErrorFormatter + } + +-- | Default formatters will just return HTTP 400 status code with error +-- message as response body. +defaultErrorFormatters :: ErrorFormatters +defaultErrorFormatters = ErrorFormatters + { bodyParserErrorFormatter = err400Formatter + , urlParseErrorFormatter = err400Formatter + , headerParseErrorFormatter = err400Formatter + , notFoundErrorFormatter = const err404 + } + +-- | A custom formatter for errors produced by parsing combinators like +-- 'ReqBody' or 'Capture'. +-- +-- A 'TypeRep' argument described the concrete combinator that raised +-- the error, allowing formatter to customize the message for different +-- combinators. +-- +-- A full 'Request' is also passed so that the formatter can react to @Accept@ header, +-- for example. +type ErrorFormatter = TypeRep -> Request -> String -> ServerError + +-- | This formatter does not get neither 'TypeRep' nor error message. +type NotFoundErrorFormatter = Request -> ServerError + +type MkContextWithErrorFormatter (ctx :: [*]) = ctx .++ DefaultErrorFormatters + +mkContextWithErrorFormatter :: forall (ctx :: [*]). Context ctx -> Context (MkContextWithErrorFormatter ctx) +mkContextWithErrorFormatter ctx = ctx .++ (defaultErrorFormatters :. EmptyContext) + +-- Internal + +err400Formatter :: ErrorFormatter +err400Formatter _ _ e = err400 { errBody = cs e } + +-- These definitions suppress "unused import" warning. +-- The imorts are needed for Haddock to correctly link to them. +_RB :: Proxy ReqBody +_RB = undefined +_C :: Proxy Capture +_C = undefined +_CT :: Proxy Context +_CT = undefined diff --git a/servant-server/src/Servant/Server/Internal/Router.hs b/servant-server/src/Servant/Server/Internal/Router.hs index d6735c9e..ecee5901 100644 --- a/servant-server/src/Servant/Server/Internal/Router.hs +++ b/servant-server/src/Servant/Server/Internal/Router.hs @@ -17,8 +17,9 @@ import Data.Text import qualified Data.Text as T import Network.Wai (Response, pathInfo) -import Servant.Server.Internal.RoutingApplication +import Servant.Server.Internal.ErrorFormatter import Servant.Server.Internal.RouteResult +import Servant.Server.Internal.RoutingApplication import Servant.Server.Internal.ServerError type Router env = Router' env RoutingApplication @@ -153,52 +154,52 @@ tweakResponse :: (RouteResult Response -> RouteResult Response) -> Router env -> tweakResponse f = fmap (\a -> \req cont -> a req (cont . f)) -- | Interpret a router as an application. -runRouter :: Router () -> RoutingApplication -runRouter r = runRouterEnv r () +runRouter :: NotFoundErrorFormatter -> Router () -> RoutingApplication +runRouter fmt r = runRouterEnv fmt r () -runRouterEnv :: Router env -> env -> RoutingApplication -runRouterEnv router env request respond = +runRouterEnv :: NotFoundErrorFormatter -> Router env -> env -> RoutingApplication +runRouterEnv fmt router env request respond = case router of StaticRouter table ls -> case pathInfo request of - [] -> runChoice ls env request respond + [] -> runChoice fmt ls env request respond -- This case is to handle trailing slashes. - [""] -> runChoice ls env request respond + [""] -> runChoice fmt ls env request respond first : rest | Just router' <- M.lookup first table -> let request' = request { pathInfo = rest } - in runRouterEnv router' env request' respond - _ -> respond $ Fail err404 + in runRouterEnv fmt router' env request' respond + _ -> respond $ Fail $ fmt request CaptureRouter router' -> case pathInfo request of - [] -> respond $ Fail err404 + [] -> respond $ Fail $ fmt request -- This case is to handle trailing slashes. - [""] -> respond $ Fail err404 + [""] -> respond $ Fail $ fmt request first : rest -> let request' = request { pathInfo = rest } - in runRouterEnv router' (first, env) request' respond + in runRouterEnv fmt router' (first, env) request' respond CaptureAllRouter router' -> let segments = pathInfo request request' = request { pathInfo = [] } - in runRouterEnv router' (segments, env) request' respond + in runRouterEnv fmt router' (segments, env) request' respond RawRouter app -> app env request respond Choice r1 r2 -> - runChoice [runRouterEnv r1, runRouterEnv r2] env request respond + runChoice fmt [runRouterEnv fmt r1, runRouterEnv fmt r2] env request respond -- | Try a list of routing applications in order. -- We stop as soon as one fails fatally or succeeds. -- If all fail normally, we pick the "best" error. -- -runChoice :: [env -> RoutingApplication] -> env -> RoutingApplication -runChoice ls = +runChoice :: NotFoundErrorFormatter -> [env -> RoutingApplication] -> env -> RoutingApplication +runChoice fmt ls = case ls of - [] -> \ _ _ respond -> respond (Fail err404) + [] -> \ _ request respond -> respond (Fail $ fmt request) [r] -> r (r : rs) -> \ env request respond -> r env request $ \ response1 -> case response1 of - Fail _ -> runChoice rs env request $ \ response2 -> + Fail _ -> runChoice fmt rs env request $ \ response2 -> respond $ highestPri response1 response2 _ -> respond response1 where diff --git a/servant-server/test/Servant/Server/RouterSpec.hs b/servant-server/test/Servant/Server/RouterSpec.hs index 472dfecc..9b69a2e7 100644 --- a/servant-server/test/Servant/Server/RouterSpec.hs +++ b/servant-server/test/Servant/Server/RouterSpec.hs @@ -32,7 +32,7 @@ routerSpec :: Spec routerSpec = do describe "tweakResponse" $ do let app' :: Application - app' = toApplication $ runRouter router' + app' = toApplication $ runRouter (const err404) router' router', router :: Router () router' = tweakResponse (fmap twk) router @@ -48,7 +48,7 @@ routerSpec = do describe "runRouter" $ do let toApp :: Router () -> Application - toApp = toApplication . runRouter + toApp = toApplication . runRouter (const err404) cap :: Router () cap = CaptureRouter $ diff --git a/servant/src/Servant/API/Header.hs b/servant/src/Servant/API/Header.hs index 14562dfc..e5ea1e00 100644 --- a/servant/src/Servant/API/Header.hs +++ b/servant/src/Servant/API/Header.hs @@ -23,7 +23,7 @@ import Servant.API.Modifiers -- >>> type MyApi = "view-my-referer" :> Header "from" Referer :> Get '[JSON] Referer type Header = Header' '[Optional, Strict] -data Header' (mods :: [*]) (sym :: Symbol) a +data Header' (mods :: [*]) (sym :: Symbol) (a :: *) deriving Typeable -- $setup From cb80fa626347dc48560e86c7fac0910da84d6c89 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 17 Jul 2020 15:51:11 +0300 Subject: [PATCH 027/156] Add tests for custom error formatters --- .../test/Servant/Server/ErrorSpec.hs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/servant-server/test/Servant/Server/ErrorSpec.hs b/servant-server/test/Servant/Server/ErrorSpec.hs index 8da38bff..72251b21 100644 --- a/servant-server/test/Servant/Server/ErrorSpec.hs +++ b/servant-server/test/Servant/Server/ErrorSpec.hs @@ -15,6 +15,8 @@ import qualified Data.ByteString.Lazy.Char8 as BCL import Data.Monoid ((<>)) import Data.Proxy +import Data.String.Conversions + (cs) import Network.HTTP.Types (hAccept, hAuthorization, hContentType, methodGet, methodPost, methodPut) @@ -31,6 +33,7 @@ spec = describe "HTTP Errors" $ do prioErrorsSpec errorRetrySpec errorChoiceSpec + customFormattersSpec -- * Auth machinery (reused throughout) @@ -293,6 +296,61 @@ errorChoiceSpec = describe "Multiple handlers return errors" `shouldRespondWith` 415 +-- }}} +------------------------------------------------------------------------------ +-- * Custom errors {{{ + +customFormatter :: ErrorFormatter +customFormatter _ _ err = err400 { errBody = "CUSTOM! " <> cs err } + +customFormatters :: ErrorFormatters +customFormatters = defaultErrorFormatters + { bodyParserErrorFormatter = customFormatter + , urlParseErrorFormatter = customFormatter + , notFoundErrorFormatter = const $ err404 { errBody = "CUSTOM! Not Found" } + } + +type CustomFormatterAPI + = "query" :> QueryParam' '[Required, Strict] "param" Int :> Get '[PlainText] String + :<|> "capture" :> Capture "cap" Bool :> Get '[PlainText] String + :<|> "body" :> ReqBody '[JSON] Int :> Post '[PlainText] String + +customFormatterAPI :: Proxy CustomFormatterAPI +customFormatterAPI = Proxy + +customFormatterServer :: Server CustomFormatterAPI +customFormatterServer = (\_ -> return "query") + :<|> (\_ -> return "capture") + :<|> (\_ -> return "body") + +customFormattersSpec :: Spec +customFormattersSpec = describe "Custom errors from combinators" + $ with (return $ serveWithContext customFormatterAPI (customFormatters :. EmptyContext) customFormatterServer) $ do + + let startsWithCustom = ResponseMatcher + { matchStatus = 400 + , matchHeaders = [] + , matchBody = MatchBody $ \_ body -> if "CUSTOM!" `BCL.isPrefixOf` body + then Nothing + else Just $ show body <> " does not start with \"CUSTOM!\"" + } + + it "formats query parse error" $ do + request methodGet "query?param=false" [] "" + `shouldRespondWith` startsWithCustom + + it "formats query parse error with missing param" $ do + request methodGet "query" [] "" + `shouldRespondWith` startsWithCustom + + it "formats capture parse error" $ do + request methodGet "capture/42" [] "" + `shouldRespondWith` startsWithCustom + + it "formats body parse error" $ do + request methodPost "body" [(hContentType, "application/json")] "foo" + `shouldRespondWith` startsWithCustom + -- }}} ------------------------------------------------------------------------------ -- * Instances {{{ From 1a09b1d3a4904f94944359f75381efca24e47a58 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 17 Jul 2020 16:01:00 +0300 Subject: [PATCH 028/156] Update GHC 8.8.x versions to 8.8.3 --- doc/cookbook/basic-auth/basic-auth.cabal | 2 +- doc/cookbook/basic-streaming/basic-streaming.cabal | 2 +- doc/cookbook/curl-mock/curl-mock.cabal | 2 +- doc/cookbook/db-postgres-pool/db-postgres-pool.cabal | 2 +- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- doc/cookbook/file-upload/file-upload.cabal | 2 +- doc/cookbook/generic/generic.cabal | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 2 +- doc/cookbook/https/https.cabal | 2 +- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- doc/cookbook/pagination/pagination.cabal | 2 +- doc/cookbook/sentry/sentry.cabal | 2 +- doc/cookbook/structuring-apis/structuring-apis.cabal | 2 +- doc/cookbook/testing/testing.cabal | 2 +- doc/cookbook/using-custom-monad/using-custom-monad.cabal | 2 +- doc/cookbook/using-free-client/using-free-client.cabal | 2 +- doc/tutorial/tutorial.cabal | 2 +- servant-client-core/servant-client-core.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-conduit/servant-conduit.cabal | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-machines/servant-machines.cabal | 2 +- servant-pipes/servant-pipes.cabal | 2 +- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/cookbook/basic-auth/basic-auth.cabal b/doc/cookbook/basic-auth/basic-auth.cabal index 1df7d086..4b3a2feb 100644 --- a/doc/cookbook/basic-auth/basic-auth.cabal +++ b/doc/cookbook/basic-auth/basic-auth.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-basic-auth main-is: BasicAuth.lhs diff --git a/doc/cookbook/basic-streaming/basic-streaming.cabal b/doc/cookbook/basic-streaming/basic-streaming.cabal index 714989b5..0f8858b3 100644 --- a/doc/cookbook/basic-streaming/basic-streaming.cabal +++ b/doc/cookbook/basic-streaming/basic-streaming.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-basic-streaming main-is: Streaming.lhs diff --git a/doc/cookbook/curl-mock/curl-mock.cabal b/doc/cookbook/curl-mock/curl-mock.cabal index 7fabc1cc..91918f38 100644 --- a/doc/cookbook/curl-mock/curl-mock.cabal +++ b/doc/cookbook/curl-mock/curl-mock.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbock-curl-mock main-is: CurlMock.lhs diff --git a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal index 41013fd1..a4cb25a9 100644 --- a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal +++ b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-db-postgres-pool main-is: PostgresPool.lhs diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index cd0cb002..06710b40 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-db-sqlite-simple main-is: DBConnection.lhs diff --git a/doc/cookbook/file-upload/file-upload.cabal b/doc/cookbook/file-upload/file-upload.cabal index b9953bff..72a511c3 100644 --- a/doc/cookbook/file-upload/file-upload.cabal +++ b/doc/cookbook/file-upload/file-upload.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-file-upload main-is: FileUpload.lhs diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal index 3a94c0ac..73188fc6 100644 --- a/doc/cookbook/generic/generic.cabal +++ b/doc/cookbook/generic/generic.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-using-custom-monad main-is: Generic.lhs diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index b2aeef30..54991be1 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -11,7 +11,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-hoist-server-with-context main-is: HoistServerWithContext.lhs diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index 080a9407..c1cec643 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-https main-is: Https.lhs diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index 51724347..aa0cb728 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -11,7 +11,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-jwt-and-basic-auth main-is: JWTAndBasicAuth.lhs diff --git a/doc/cookbook/pagination/pagination.cabal b/doc/cookbook/pagination/pagination.cabal index 74a47d7b..0c20e0d2 100644 --- a/doc/cookbook/pagination/pagination.cabal +++ b/doc/cookbook/pagination/pagination.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-pagination main-is: Pagination.lhs diff --git a/doc/cookbook/sentry/sentry.cabal b/doc/cookbook/sentry/sentry.cabal index 03d0acc4..2ac54383 100644 --- a/doc/cookbook/sentry/sentry.cabal +++ b/doc/cookbook/sentry/sentry.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-sentry main-is: Sentry.lhs diff --git a/doc/cookbook/structuring-apis/structuring-apis.cabal b/doc/cookbook/structuring-apis/structuring-apis.cabal index 810a69ad..6355d507 100644 --- a/doc/cookbook/structuring-apis/structuring-apis.cabal +++ b/doc/cookbook/structuring-apis/structuring-apis.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-structuring-apis main-is: StructuringApis.lhs diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index 6ba8f785..9e13ce60 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -10,7 +10,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-testing main-is: Testing.lhs diff --git a/doc/cookbook/using-custom-monad/using-custom-monad.cabal b/doc/cookbook/using-custom-monad/using-custom-monad.cabal index 232c8d8b..244ab5ed 100644 --- a/doc/cookbook/using-custom-monad/using-custom-monad.cabal +++ b/doc/cookbook/using-custom-monad/using-custom-monad.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-using-custom-monad main-is: UsingCustomMonad.lhs diff --git a/doc/cookbook/using-free-client/using-free-client.cabal b/doc/cookbook/using-free-client/using-free-client.cabal index c8a07f53..e079cd7c 100644 --- a/doc/cookbook/using-free-client/using-free-client.cabal +++ b/doc/cookbook/using-free-client/using-free-client.cabal @@ -8,7 +8,7 @@ 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.4.4, GHC==8.6.5, GHC==8.8.3 executable cookbook-using-free-client main-is: UsingFreeClient.lhs diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index b338180d..dcbae743 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -17,7 +17,7 @@ tested-with: GHC==8.2.2 GHC==8.4.4 GHC==8.6.5 - GHC==8.8.2 + GHC==8.8.3 extra-source-files: static/index.html static/ui.js diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index a3b44dca..0d123e61 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -21,7 +21,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 , GHCJS == 8.4 extra-source-files: diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index d920577b..28003268 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -25,7 +25,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index df07ace7..829873b6 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -22,7 +22,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index b54c9c3e..0697b20d 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -24,7 +24,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 5ab34718..640b6994 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -26,7 +26,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index f21dcc73..8d49ef18 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -25,7 +25,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 0603ff58..5b376429 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -22,7 +22,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 79ca52f4..16ba7c72 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -22,7 +22,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 374220ef..06708d72 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -29,7 +29,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 extra-source-files: CHANGELOG.md diff --git a/servant/servant.cabal b/servant/servant.cabal index b0c92303..20dbc931 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -25,7 +25,7 @@ tested-with: || ==8.2.2 || ==8.4.4 || ==8.6.5 - || ==8.8.2 + || ==8.8.3 , GHCJS == 8.4 extra-source-files: From 7218c66fd004774668a903a298e0303484cae02c Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 17 Jul 2020 16:01:16 +0300 Subject: [PATCH 029/156] haskell-ci regenerate --- .travis.yml | 117 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ef279d7..6ffea7d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,13 @@ # # haskell-ci '--config=cabal.haskell-ci' '--output=.travis.yml' 'cabal.project' # +# To regenerate the script (for example after adjusting tested-with) run +# +# haskell-ci regenerate +# # For more information, see https://github.com/haskell-CI/haskell-ci # -# version: 0.9.20200121 +# version: 0.10.1 # version: ~> 1.0 language: c @@ -37,23 +41,20 @@ jobs: - 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.10.1 - 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.10.1","cabal-install-3.2"]}} - 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"]}} + - compiler: ghc-8.8.3 + 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.3","cabal-install-3.2"]}} 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"]}} + 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.2"]}} 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"]}} + 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.2"]}} 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"]}} + 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.2"]}} 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"]}} + 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.2"]}} os: linux before_install: - | @@ -110,9 +111,9 @@ install: - 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.3.*') ; 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 + - if ! $GHCJS ; then ${CABAL} v2-install $WITHCOMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.16.3' ; fi + - if $GHCJS ; then ${CABAL} v2-install -w ghc-8.4.4 --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' ; fi + - if $GHCJS ; then ${CABAL} v2-install -w ghc-8.4.4 --ignore-project hspec-discover ; fi # Generate cabal.project - rm -rf cabal.project cabal.project.local cabal.project.freeze - touch cabal.project @@ -139,6 +140,50 @@ install: 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 + - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client-core' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-http-streams' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-docs' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-foreign' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-server' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package tutorial' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-machines' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-conduit' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-pipes' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-file-upload' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-generic' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - | echo "constraints: foundation >=0.0.14" >> cabal.project echo "constraints: memory <0.14.12 || >0.14.12" >> cabal.project @@ -236,6 +281,50 @@ script: 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 + - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client-core' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-http-streams' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-docs' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-foreign' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-server' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package tutorial' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-machines' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-conduit' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-pipes' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-file-upload' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-generic' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - | echo "constraints: foundation >=0.0.14" >> cabal.project echo "constraints: memory <0.14.12 || >0.14.12" >> cabal.project @@ -303,5 +392,5 @@ script: - 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"]) +# REGENDATA ("0.10.1",["--config=cabal.haskell-ci","--output=.travis.yml","cabal.project"]) # EOF From bd2a813c1a1c7a83341c9d0e9cc2dd1007c0e1eb Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 17 Jul 2020 16:09:56 +0300 Subject: [PATCH 030/156] TEMP disable cookbook/testing --- .travis.yml | 13 ++----------- cabal.project | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ffea7d4..3a2cae4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -136,7 +136,6 @@ install: 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 @@ -176,8 +175,6 @@ install: - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi @@ -193,7 +190,7 @@ install: 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" + - "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-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 @@ -214,7 +211,6 @@ install: - 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 @@ -251,7 +247,6 @@ script: - 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.]*')" @@ -277,7 +272,6 @@ script: 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 @@ -317,8 +311,6 @@ script: - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi @@ -334,7 +326,7 @@ script: 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" + - "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-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 - | @@ -358,7 +350,6 @@ script: 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} ;; diff --git a/cabal.project b/cabal.project index 295938f1..72c28f93 100644 --- a/cabal.project +++ b/cabal.project @@ -32,7 +32,7 @@ packages: -- doc/cookbook/jwt-and-basic-auth/ doc/cookbook/pagination -- doc/cookbook/sentry - doc/cookbook/testing +-- doc/cookbook/testing doc/cookbook/structuring-apis doc/cookbook/using-custom-monad doc/cookbook/using-free-client From d94ad9df9bf8cea68a6a8e585801229dc5eac458 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 17 Jul 2020 17:06:13 +0300 Subject: [PATCH 031/156] Add cookbook entry for custom error formatters --- .travis.yml | 13 +- cabal.project | 1 + doc/cookbook/custom-errors/CustomErrors.lhs | 189 ++++++++++++++++++ .../custom-errors/custom-errors.cabal | 25 +++ doc/cookbook/index.rst | 1 + 5 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 doc/cookbook/custom-errors/CustomErrors.lhs create mode 100644 doc/cookbook/custom-errors/custom-errors.cabal diff --git a/.travis.yml b/.travis.yml index 3a2cae4a..f4bcbd1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -131,6 +131,7 @@ install: 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/custom-errors" >> 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 @@ -165,6 +166,8 @@ install: - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi @@ -190,7 +193,7 @@ install: 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-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" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|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 @@ -206,6 +209,7 @@ install: - 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/custom-errors/configure.ac" ]; then (cd "doc/cookbook/custom-errors" && 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 @@ -242,6 +246,7 @@ script: - 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_custom_errors="$(find . -maxdepth 1 -type d -regex '.*/cookbook-custom-errors-[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.]*')" @@ -267,6 +272,7 @@ script: 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_custom_errors}" >> 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 @@ -301,6 +307,8 @@ script: - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi @@ -326,7 +334,7 @@ script: 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-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" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|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 - | @@ -345,6 +353,7 @@ script: servant-pipes) echo ${PKGDIR_servant_pipes} ;; cookbook-basic-auth) echo ${PKGDIR_cookbook_basic_auth} ;; cookbook-curl-mock) echo ${PKGDIR_cookbook_curl_mock} ;; + cookbook-custom-errors) echo ${PKGDIR_cookbook_custom_errors} ;; 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} ;; diff --git a/cabal.project b/cabal.project index 72c28f93..fcc8c291 100644 --- a/cabal.project +++ b/cabal.project @@ -22,6 +22,7 @@ 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 diff --git a/doc/cookbook/custom-errors/CustomErrors.lhs b/doc/cookbook/custom-errors/CustomErrors.lhs new file mode 100644 index 00000000..4e8b773c --- /dev/null +++ b/doc/cookbook/custom-errors/CustomErrors.lhs @@ -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. diff --git a/doc/cookbook/custom-errors/custom-errors.cabal b/doc/cookbook/custom-errors/custom-errors.cabal new file mode 100644 index 00000000..1190c1a6 --- /dev/null +++ b/doc/cookbook/custom-errors/custom-errors.cabal @@ -0,0 +1,25 @@ +name: cookbook-custom-errors +version: 0.1 +synopsis: Return custom error messages from combinators +homepage: http://docs.servant.dev +license: BSD3 +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.3 + +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 diff --git a/doc/cookbook/index.rst b/doc/cookbook/index.rst index ac0ed5cf..acd9efe3 100644 --- a/doc/cookbook/index.rst +++ b/doc/cookbook/index.rst @@ -25,6 +25,7 @@ you name it! db-postgres-pool/PostgresPool.lhs using-custom-monad/UsingCustomMonad.lhs using-free-client/UsingFreeClient.lhs + custom-errors/CustomErrors.lhs basic-auth/BasicAuth.lhs basic-streaming/Streaming.lhs jwt-and-basic-auth/JWTAndBasicAuth.lhs From cb0224d06396731955f8c0c81d298d378079e6fc Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 17 Jul 2020 17:17:45 +0300 Subject: [PATCH 032/156] Add 8.10.1 to tested-with, haskell-ci regenerate --- .travis.yml | 3 +++ doc/cookbook/basic-auth/basic-auth.cabal | 2 +- doc/cookbook/basic-streaming/basic-streaming.cabal | 2 +- doc/cookbook/curl-mock/curl-mock.cabal | 2 +- doc/cookbook/custom-errors/custom-errors.cabal | 2 +- doc/cookbook/db-postgres-pool/db-postgres-pool.cabal | 2 +- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- doc/cookbook/file-upload/file-upload.cabal | 2 +- doc/cookbook/generic/generic.cabal | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 2 +- doc/cookbook/https/https.cabal | 2 +- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- doc/cookbook/pagination/pagination.cabal | 2 +- doc/cookbook/sentry/sentry.cabal | 2 +- doc/cookbook/structuring-apis/structuring-apis.cabal | 2 +- doc/cookbook/testing/testing.cabal | 2 +- doc/cookbook/using-custom-monad/using-custom-monad.cabal | 2 +- doc/cookbook/using-free-client/using-free-client.cabal | 2 +- doc/tutorial/tutorial.cabal | 2 +- servant-client-core/servant-client-core.cabal | 1 + servant-client/servant-client.cabal | 1 + servant-conduit/servant-conduit.cabal | 1 + servant-docs/servant-docs.cabal | 1 + servant-foreign/servant-foreign.cabal | 1 + servant-http-streams/servant-http-streams.cabal | 1 + servant-machines/servant-machines.cabal | 1 + servant-pipes/servant-pipes.cabal | 1 + servant-server/servant-server.cabal | 1 + servant/servant.cabal | 1 + 29 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4bcbd1f..0944531f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,9 @@ jobs: - 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.10.1 + 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.10.1","cabal-install-3.2"]}} + os: linux - compiler: ghc-8.8.3 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.3","cabal-install-3.2"]}} os: linux diff --git a/doc/cookbook/basic-auth/basic-auth.cabal b/doc/cookbook/basic-auth/basic-auth.cabal index 4b3a2feb..d3f41059 100644 --- a/doc/cookbook/basic-auth/basic-auth.cabal +++ b/doc/cookbook/basic-auth/basic-auth.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-basic-auth main-is: BasicAuth.lhs diff --git a/doc/cookbook/basic-streaming/basic-streaming.cabal b/doc/cookbook/basic-streaming/basic-streaming.cabal index 0f8858b3..bfbf5331 100644 --- a/doc/cookbook/basic-streaming/basic-streaming.cabal +++ b/doc/cookbook/basic-streaming/basic-streaming.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-basic-streaming main-is: Streaming.lhs diff --git a/doc/cookbook/curl-mock/curl-mock.cabal b/doc/cookbook/curl-mock/curl-mock.cabal index 91918f38..c0322413 100644 --- a/doc/cookbook/curl-mock/curl-mock.cabal +++ b/doc/cookbook/curl-mock/curl-mock.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbock-curl-mock main-is: CurlMock.lhs diff --git a/doc/cookbook/custom-errors/custom-errors.cabal b/doc/cookbook/custom-errors/custom-errors.cabal index 1190c1a6..d43b5102 100644 --- a/doc/cookbook/custom-errors/custom-errors.cabal +++ b/doc/cookbook/custom-errors/custom-errors.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-custom-errors main-is: CustomErrors.lhs diff --git a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal index a4cb25a9..8d0a6eb0 100644 --- a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal +++ b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-db-postgres-pool main-is: PostgresPool.lhs diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index 06710b40..e8e1588f 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-db-sqlite-simple main-is: DBConnection.lhs diff --git a/doc/cookbook/file-upload/file-upload.cabal b/doc/cookbook/file-upload/file-upload.cabal index 72a511c3..d589912c 100644 --- a/doc/cookbook/file-upload/file-upload.cabal +++ b/doc/cookbook/file-upload/file-upload.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-file-upload main-is: FileUpload.lhs diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal index 73188fc6..5081a981 100644 --- a/doc/cookbook/generic/generic.cabal +++ b/doc/cookbook/generic/generic.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-using-custom-monad main-is: Generic.lhs diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index 54991be1..06bdc5c8 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -11,7 +11,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-hoist-server-with-context main-is: HoistServerWithContext.lhs diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index c1cec643..ec778b1b 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-https main-is: Https.lhs diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index aa0cb728..dcbb95ba 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -11,7 +11,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-jwt-and-basic-auth main-is: JWTAndBasicAuth.lhs diff --git a/doc/cookbook/pagination/pagination.cabal b/doc/cookbook/pagination/pagination.cabal index 0c20e0d2..2e021dc0 100644 --- a/doc/cookbook/pagination/pagination.cabal +++ b/doc/cookbook/pagination/pagination.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-pagination main-is: Pagination.lhs diff --git a/doc/cookbook/sentry/sentry.cabal b/doc/cookbook/sentry/sentry.cabal index 2ac54383..41750a45 100644 --- a/doc/cookbook/sentry/sentry.cabal +++ b/doc/cookbook/sentry/sentry.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-sentry main-is: Sentry.lhs diff --git a/doc/cookbook/structuring-apis/structuring-apis.cabal b/doc/cookbook/structuring-apis/structuring-apis.cabal index 6355d507..431950f1 100644 --- a/doc/cookbook/structuring-apis/structuring-apis.cabal +++ b/doc/cookbook/structuring-apis/structuring-apis.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-structuring-apis main-is: StructuringApis.lhs diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index 9e13ce60..b69aeae1 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -10,7 +10,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-testing main-is: Testing.lhs diff --git a/doc/cookbook/using-custom-monad/using-custom-monad.cabal b/doc/cookbook/using-custom-monad/using-custom-monad.cabal index 244ab5ed..022a1ad8 100644 --- a/doc/cookbook/using-custom-monad/using-custom-monad.cabal +++ b/doc/cookbook/using-custom-monad/using-custom-monad.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-using-custom-monad main-is: UsingCustomMonad.lhs diff --git a/doc/cookbook/using-free-client/using-free-client.cabal b/doc/cookbook/using-free-client/using-free-client.cabal index e079cd7c..51ac1fc0 100644 --- a/doc/cookbook/using-free-client/using-free-client.cabal +++ b/doc/cookbook/using-free-client/using-free-client.cabal @@ -8,7 +8,7 @@ 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.3 +tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-using-free-client main-is: UsingFreeClient.lhs diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index dcbae743..82162ffd 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -17,7 +17,7 @@ tested-with: GHC==8.2.2 GHC==8.4.4 GHC==8.6.5 - GHC==8.8.3 + GHC==8.8.3, GHC ==8.10.1 extra-source-files: static/index.html static/ui.js diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 0d123e61..1d1618ce 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -22,6 +22,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 , GHCJS == 8.4 extra-source-files: diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 28003268..20c7e522 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -26,6 +26,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 829873b6..d6bdf0a0 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -23,6 +23,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 0697b20d..c061cc8e 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -25,6 +25,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 640b6994..a0531c38 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -27,6 +27,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 8d49ef18..041919e2 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -26,6 +26,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 5b376429..a4103947 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -23,6 +23,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 16ba7c72..d0c82340 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -23,6 +23,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 06708d72..c0a42da4 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -30,6 +30,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 extra-source-files: CHANGELOG.md diff --git a/servant/servant.cabal b/servant/servant.cabal index 20dbc931..c2f5c090 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -26,6 +26,7 @@ tested-with: || ==8.4.4 || ==8.6.5 || ==8.8.3 + || ==8.10.1 , GHCJS == 8.4 extra-source-files: From 55f5a78b1bb1116e58094cf6c26aa4f7fc23a53e Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 21 Jul 2020 01:02:05 +0200 Subject: [PATCH 033/156] Docs: Hoist Server "Footnote" --- .../hoist-server-with-context/HoistServerWithContext.lhs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs b/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs index 23d08462..2c50c2e9 100644 --- a/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs +++ b/doc/cookbook/hoist-server-with-context/HoistServerWithContext.lhs @@ -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 From 43cf589e0ec071d8c4d5f254a546fe1998ed055b Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Thu, 30 Jul 2020 19:03:58 +0300 Subject: [PATCH 034/156] Bump version to 0.18 --- .travis.yml | 8 ++++++++ cabal.project | 6 ++++++ servant-client-core/servant-client-core.cabal | 4 ++-- servant-client/servant-client.cabal | 10 +++++----- servant-conduit/servant-conduit.cabal | 6 +++--- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 10 +++++----- servant-machines/servant-machines.cabal | 6 +++--- servant-pipes/servant-pipes.cabal | 6 +++--- servant-server/servant-server.cabal | 4 ++-- servant/servant.cabal | 2 +- 12 files changed, 40 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0944531f..4db8acad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -195,6 +195,10 @@ install: 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 "allow-newer: servant-multipart:servant" >> cabal.project + echo "allow-newer: servant-multipart:servant-server" >> cabal.project + echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project + echo "allow-newer: servant-js:servant" >> 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-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|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 @@ -336,6 +340,10 @@ script: 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 "allow-newer: servant-multipart:servant" >> cabal.project + echo "allow-newer: servant-multipart:servant-server" >> cabal.project + echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project + echo "allow-newer: servant-js:servant" >> 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-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|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 diff --git a/cabal.project b/cabal.project index fcc8c291..7ac8231c 100644 --- a/cabal.project +++ b/cabal.project @@ -63,3 +63,9 @@ 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 + +-- https://github.com/haskell-servant/servant-multipart/pull/41 +allow-newer: servant-multipart:servant +allow-newer: servant-multipart:servant-server +allow-newer: servant-multipart:servant-client-core +allow-newer: servant-js:servant diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 1d1618ce..bb684c29 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client-core -version: 0.17 +version: 0.18 synopsis: Core functionality and class for client function generation for servant APIs category: Servant, Web @@ -70,7 +70,7 @@ library -- Servant dependencies build-depends: - servant >= 0.17 && <0.18 + servant >= 0.18 && <0.19 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 20c7e522..bd5fb453 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client -version: 0.17 +version: 0.18 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -63,8 +63,8 @@ library -- Servant dependencies. -- Strict dependency on `servant-client-core` as we re-export things. build-depends: - servant == 0.17.* - , servant-client-core >= 0.17 && <0.17.1 + servant == 0.18.* + , servant-client-core >= 0.18 && <0.18.1 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. @@ -128,8 +128,8 @@ test-suite spec , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 , QuickCheck >= 2.12.6.1 && < 2.14 - , servant == 0.17.* - , servant-server == 0.17.* + , servant == 0.18.* + , servant-server == 0.18.* , tdigest >= 0.2 && < 0.3 build-tool-depends: diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index d6bdf0a0..11db60b7 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -40,7 +40,7 @@ library , conduit >=1.3.1 && <1.4 , mtl >=2.2.2 && <2.3 , resourcet >=1.2.2 && <1.3 - , servant >=0.15 && <0.18 + , servant >=0.15 && <0.19 , unliftio-core >=0.1.2.0 && <0.3 hs-source-dirs: src default-language: Haskell2010 @@ -61,8 +61,8 @@ test-suite example , resourcet , servant , servant-conduit - , servant-server >=0.15 && <0.18 - , servant-client >=0.15 && <0.18 + , servant-server >=0.15 && <0.19 + , servant-client >=0.15 && <0.19 , wai >=3.2.1.2 && <3.3 , warp >=3.2.25 && <3.4 , http-client diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index c061cc8e..f93272f6 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -53,7 +53,7 @@ library -- Servant dependencies build-depends: - servant >= 0.17 && <0.18 + servant >= 0.18 && <0.19 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index a0531c38..6a2cc0a0 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -52,7 +52,7 @@ library -- Servant dependencies build-depends: - servant >=0.17 && <0.18 + servant >=0.18 && <0.19 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 041919e2..62ebffa8 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-http-streams -version: 0.17 +version: 0.18 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -60,8 +60,8 @@ library -- Servant dependencies. -- Strict dependency on `servant-client-core` as we re-export things. build-depends: - servant == 0.17.* - , servant-client-core >= 0.17 && <0.17.1 + servant == 0.18.* + , servant-client-core >= 0.18 && <0.19.1 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. @@ -122,8 +122,8 @@ test-suite spec , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 , QuickCheck >= 2.12.6.1 && < 2.14 - , servant == 0.17.* - , servant-server == 0.17.* + , servant == 0.18.* + , servant-server == 0.18.* , tdigest >= 0.2 && < 0.3 build-tool-depends: diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index a4103947..c6ada8c5 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -39,7 +39,7 @@ library , bytestring >=0.10.8.1 && <0.11 , machines >=0.6.4 && <0.8 , mtl >=2.2.2 && <2.3 - , servant >=0.15 && <0.18 + , servant >=0.15 && <0.19 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall @@ -58,8 +58,8 @@ test-suite example , servant , machines , servant-machines - , servant-server >=0.15 && <0.18 - , servant-client >=0.15 && <0.18 + , servant-server >=0.15 && <0.19 + , servant-client >=0.15 && <0.19 , wai >=3.2.1.2 && <3.3 , warp >=3.2.25 && <3.4 , http-client diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index d0c82340..cdabca7e 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -41,7 +41,7 @@ library , pipes-safe >=2.3.1 && <2.4 , mtl >=2.2.2 && <2.3 , monad-control >=1.0.2.3 && <1.1 - , servant >=0.15 && <0.18 + , servant >=0.15 && <0.19 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall @@ -62,8 +62,8 @@ test-suite example , pipes-safe , servant-pipes , pipes-bytestring >=2.1.6 && <2.2 - , servant-server >=0.15 && <0.18 - , servant-client >=0.15 && <0.18 + , servant-server >=0.15 && <0.19 + , servant-client >=0.15 && <0.19 , wai >=3.2.1.2 && <3.3 , warp >=3.2.25 && <3.4 , http-client diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index c0a42da4..d16ea789 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-server -version: 0.17 +version: 0.18 x-revision: 1 synopsis: A family of combinators for defining webservices APIs and serving them @@ -77,7 +77,7 @@ library -- Servant dependencies -- strict dependency as we re-export 'servant' things. build-depends: - servant >= 0.17 && < 0.17.1 + servant >= 0.18 && < 0.19.1 , http-api-data >= 0.4.1 && < 0.4.2 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. diff --git a/servant/servant.cabal b/servant/servant.cabal index c2f5c090..f5d821b1 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant -version: 0.17 +version: 0.18 synopsis: A family of combinators for defining webservices APIs category: Servant, Web From d740c189926ab2a85f3a813371fa22aa5479451e Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Thu, 30 Jul 2020 19:05:46 +0300 Subject: [PATCH 035/156] Explicit export list in ErrorFormatter.hs --- .../Servant/Server/Internal/ErrorFormatter.hs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs b/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs index c5a7b221..26a7e85b 100644 --- a/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs +++ b/servant-server/src/Servant/Server/Internal/ErrorFormatter.hs @@ -1,11 +1,19 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeOperators #-} module Servant.Server.Internal.ErrorFormatter - where + ( ErrorFormatters(..) + , ErrorFormatter + , NotFoundErrorFormatter + + , DefaultErrorFormatters + , defaultErrorFormatters + + , MkContextWithErrorFormatter + , mkContextWithErrorFormatter + ) where import Data.String.Conversions (cs) @@ -72,8 +80,8 @@ err400Formatter _ _ e = err400 { errBody = cs e } -- These definitions suppress "unused import" warning. -- The imorts are needed for Haddock to correctly link to them. _RB :: Proxy ReqBody -_RB = undefined +_RB = Proxy _C :: Proxy Capture -_C = undefined +_C = Proxy _CT :: Proxy Context -_CT = undefined +_CT = Proxy From e93376939c8703910e5a78e2151a83f2cd018d57 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 31 Jul 2020 13:32:17 +0300 Subject: [PATCH 036/156] Reenable cookbook-testing --- .travis.yml | 13 +++++++++++-- cabal.project | 2 +- doc/cookbook/testing/testing.cabal | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4db8acad..9cd2a0a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -140,6 +140,7 @@ install: 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 @@ -181,6 +182,8 @@ install: - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi @@ -200,7 +203,7 @@ install: echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project echo "allow-newer: servant-js:servant" >> 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-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|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" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|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 @@ -222,6 +225,7 @@ install: - 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 @@ -259,6 +263,7 @@ script: - 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.]*')" @@ -285,6 +290,7 @@ script: 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 @@ -326,6 +332,8 @@ script: - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi + - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi @@ -345,7 +353,7 @@ script: echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project echo "allow-newer: servant-js:servant" >> 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-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|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" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|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 - | @@ -370,6 +378,7 @@ script: 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} ;; diff --git a/cabal.project b/cabal.project index 7ac8231c..93809864 100644 --- a/cabal.project +++ b/cabal.project @@ -33,7 +33,7 @@ packages: -- doc/cookbook/jwt-and-basic-auth/ doc/cookbook/pagination -- doc/cookbook/sentry --- doc/cookbook/testing + doc/cookbook/testing doc/cookbook/structuring-apis doc/cookbook/using-custom-monad doc/cookbook/using-free-client diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index b69aeae1..6d9c59a7 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -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 From 6dcb29badabef60977194c2841a39ccd7cd79833 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 31 Jul 2020 20:19:07 +0200 Subject: [PATCH 037/156] Update changelogs. --- servant-client-core/CHANGELOG.md | 8 ++++++++ servant-client/CHANGELOG.md | 8 ++++++++ servant-http-streams/CHANGELOG.md | 8 ++++++++ servant-server/CHANGELOG.md | 11 +++++++++++ servant/CHANGELOG.md | 22 +++++++++++++++++++++- 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/servant-client-core/CHANGELOG.md b/servant-client-core/CHANGELOG.md index b69c3235..e0adbef4 100644 --- a/servant-client-core/CHANGELOG.md +++ b/servant-client-core/CHANGELOG.md @@ -1,6 +1,14 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18 +---- + +### Significant changes + +- Support for ghc8.8 (#1318, #1326, #1327) + + 0.17 ---- diff --git a/servant-client/CHANGELOG.md b/servant-client/CHANGELOG.md index adc6ef56..5a056ad5 100644 --- a/servant-client/CHANGELOG.md +++ b/servant-client/CHANGELOG.md @@ -1,6 +1,14 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18 +---- + +### Significant changes + +- Support for ghc8.8 (#1318, #1326, #1327) + + 0.17 ---- diff --git a/servant-http-streams/CHANGELOG.md b/servant-http-streams/CHANGELOG.md index 98619531..eaef7f39 100644 --- a/servant-http-streams/CHANGELOG.md +++ b/servant-http-streams/CHANGELOG.md @@ -1,6 +1,14 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-http-streams/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18 +---- + +### Significant changes + +- Support for ghc8.8 (#1318, #1326, #1327) + + 0.17 ---- diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index db6afd3e..44680769 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -1,6 +1,17 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18 +---- + +### Significant changes + +- Support for ghc8.8 (#1318, #1326, #1327) + +- Configurable error messages for automatic errors thrown by servant, + like "no route" or "could not parse json body" (#1312, #1326, #1327) + + 0.17 ---- diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index dc3e783e..c9d72a70 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -1,5 +1,25 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18 +---- + +### Significant changes + +- Support for ghc8.8 (#1318, #1326, #1327) + +- Configurable error messages for automatic errors thrown by servant, + like "no route" or "could not parse json body" (#1312, #1326, #1327) + +### Other changes + +- Witness that a type-level natural number corresponds to a HTTP + status code (#1310) + +- Improve haddocs (#1279) + +- Dependency management (#1269, #1293, #1286, #1287) + + 0.17 ---- @@ -53,7 +73,7 @@ can be run other ways too (though not so conveniently). - *servant* Remove deprecated modules [1268#](https://github.com/haskell-servant/servant/pull/1268) - + - `Servant.Utils.Links` is `Servant.Links` - `Servant.API.Internal.Test.ComprehensiveAPI` is `Servant.Test.ComprehensiveAPI` From 858fb6cce5b3fbe0cd74db2d32ce50a639cb08ee Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Fri, 31 Jul 2020 20:25:31 +0200 Subject: [PATCH 038/156] Fix: remove x-revision from servant-server.cabal. --- servant-server/servant-server.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index d16ea789..5f7268d6 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,7 +1,6 @@ cabal-version: >=1.10 name: servant-server version: 0.18 -x-revision: 1 synopsis: A family of combinators for defining webservices APIs and serving them category: Servant, Web From 9f8127ed54aac59236a656c88cdffe8d6d0e4866 Mon Sep 17 00:00:00 2001 From: Jordan Kaye Date: Sun, 2 Aug 2020 05:51:08 -0600 Subject: [PATCH 039/156] Minor rewording in Server tutorial Fixed an awkward wording in the Server tutorial. --- doc/tutorial/Server.lhs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tutorial/Server.lhs b/doc/tutorial/Server.lhs index 61769a30..265bd707 100644 --- a/doc/tutorial/Server.lhs +++ b/doc/tutorial/Server.lhs @@ -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 From b4b649c8f41bd862e108a1b4f190a4ecfedd8389 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sun, 30 Aug 2020 15:26:48 +0800 Subject: [PATCH 040/156] Allow http-api-data 0.4.2 Builds fine and all tests pass here. --- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 5f7268d6..74eeb3ad 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -77,7 +77,7 @@ library -- strict dependency as we re-export 'servant' things. build-depends: servant >= 0.18 && < 0.19.1 - , http-api-data >= 0.4.1 && < 0.4.2 + , http-api-data >= 0.4.1 && < 0.4.3 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant/servant.cabal b/servant/servant.cabal index f5d821b1..a8b37b38 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -91,7 +91,7 @@ library -- We depend (heavily) on the API of these packages: -- i.e. re-export, or allow using without direct dependency build-depends: - http-api-data >= 0.4.1 && < 0.4.2 + http-api-data >= 0.4.1 && < 0.4.3 , singleton-bool >= 0.1.4 && < 0.1.6 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. From 2906f0137c5eaf4bdf0f38d35ccece2314459c6f Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Tue, 1 Sep 2020 12:21:57 +0200 Subject: [PATCH 041/156] Bump minor version of servant-conduit. --- servant-conduit/servant-conduit.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 11db60b7..424a2d03 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-conduit -version: 0.15 +version: 0.15.1 x-revision: 3 synopsis: Servant Stream support for conduit. From 27f96628306f7aa14db921eae93a0679572a21a6 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Tue, 1 Sep 2020 12:24:38 +0200 Subject: [PATCH 042/156] Remove x-revision in servant-conduit. (Oops.) --- servant-conduit/servant-conduit.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 424a2d03..83a504fd 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -1,7 +1,6 @@ cabal-version: >=1.10 name: servant-conduit version: 0.15.1 -x-revision: 3 synopsis: Servant Stream support for conduit. category: Servant, Web, Enumerator From 1760cc852737be49e8d4471db972672b476ca551 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Tue, 1 Sep 2020 14:21:26 +0200 Subject: [PATCH 043/156] Bump more package versions. --- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-machines/servant-machines.cabal | 3 +-- servant-pipes/servant-pipes.cabal | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index f93272f6..0e260f35 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-docs -version: 0.11.5 +version: 0.11.6 synopsis: generate API docs for your servant webservice category: Servant, Web diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 6a2cc0a0..322faa64 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-foreign -version: 0.15.1 +version: 0.15.2 synopsis: Helpers for generating clients for servant APIs in any programming language category: Servant, Web diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index c6ada8c5..79401a19 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -1,7 +1,6 @@ cabal-version: >=1.10 name: servant-machines -version: 0.15 -x-revision: 4 +version: 0.15.1 synopsis: Servant Stream support for machines category: Servant, Web, Enumerator diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index cdabca7e..2ef5969a 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -1,7 +1,6 @@ cabal-version: >=1.10 name: servant-pipes -version: 0.15.1 -x-revision: 1 +version: 0.15.2 synopsis: Servant Stream support for pipes category: Servant, Web, Pipes From e3a29addf43b0e35feb114e42ef18efdaba7219f Mon Sep 17 00:00:00 2001 From: Szabo Gergely Date: Thu, 3 Sep 2020 13:43:10 +0900 Subject: [PATCH 044/156] Fix servant-docs code sample in README (#1335) --- servant-docs/README.md | 55 +++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/servant-docs/README.md b/servant-docs/README.md index 9c2d6e37..231e6be4 100644 --- a/servant-docs/README.md +++ b/servant-docs/README.md @@ -2,24 +2,56 @@ ![servant](https://raw.githubusercontent.com/haskell-servant/servant/master/servant.png) -Generate API docs for your *servant* webservice. Feel free to also take a look at [servant-pandoc](https://github.com/mpickering/servant-pandoc) which uses this package to target a broad range of output formats using the excellent **pandoc**. +Generate API docs for your _servant_ webservice. Feel free to also take a look at [servant-pandoc](https://github.com/mpickering/servant-pandoc) which uses this package to target a broad range of output formats using the excellent **pandoc**. ## Example See [here](https://github.com/haskell-servant/servant/blob/master/servant-docs/example/greet.md) for the output of the following program. -``` haskell +```haskell {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} -import Data.Proxy -import Data.Text +import Data.Aeson (FromJSON, ToJSON) +import Data.Proxy (Proxy (..)) +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) +import Servant.API + ( (:<|>), + (:>), + Capture, + Delete, + Get, + JSON, + MimeRender, + PlainText, + Post, + QueryParam, + ReqBody, + mimeRender, + ) import Servant.Docs + ( API, + DocCapture (..), + DocQueryParam (..), + ParamKind (..), + ToCapture, + ToParam, + ToSample, + docs, + markdown, + singleSample, + toCapture, + toParam, + toSamples, + ) -- our type for a Greeting message data Greet = Greet { _msg :: Text } @@ -29,6 +61,7 @@ data Greet = Greet { _msg :: Text } -- 'MimeRender' instance for 'JSON'. instance FromJSON Greet instance ToJSON Greet +instance ToSample () -- We can also implement 'MimeRender' explicitly for additional formats. instance MimeRender PlainText Greet where @@ -36,8 +69,7 @@ instance MimeRender PlainText Greet where -- we provide a sample value for the 'Greet' type instance ToSample Greet where - toSample = Just g - + toSamples _ = singleSample g where g = Greet "Hello, haskeller!" instance ToParam (QueryParam "capital" Bool) where @@ -45,6 +77,7 @@ instance ToParam (QueryParam "capital" Bool) where DocQueryParam "capital" ["true", "false"] "Get the greeting message in uppercase (true) or not (false). Default is false." + Normal instance ToCapture (Capture "name" Text) where toCapture _ = DocCapture "name" "name of the person to greet" @@ -55,8 +88,8 @@ instance ToCapture (Capture "greetid" Text) where -- API specification type TestApi = "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON,PlainText] Greet - :<|> "greet" :> RQBody '[JSON] Greet :> Post '[JSON] Greet - :<|> "delete" :> Capture "greetid" Text :> Delete '[] () + :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet + :<|> "delete" :> Capture "greetid" Text :> Delete '[JSON] () testApi :: Proxy TestApi testApi = Proxy From e364470dd96a4bbf6906fe2d8ecb912532960d22 Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Thu, 1 Oct 2020 00:58:50 -0700 Subject: [PATCH 045/156] Fix docs: emptyAPIServer -> emptyServer (#1344) --- servant-server/src/Servant/Server/Internal.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index 9fa0187b..a234f145 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -750,12 +750,12 @@ data EmptyServer = EmptyServer deriving (Typeable, Eq, Show, Bounded, Enum) emptyServer :: ServerT EmptyAPI m emptyServer = Tagged EmptyServer --- | The server for an `EmptyAPI` is `emptyAPIServer`. +-- | The server for an `EmptyAPI` is `emptyServer`. -- -- > type MyApi = "nothing" :> EmptyApi -- > -- > server :: Server MyApi --- > server = emptyAPIServer +-- > server = emptyServer instance HasServer EmptyAPI context where type ServerT EmptyAPI m = Tagged m EmptyServer From 264846a61f6e8919278be4002c77d48c936be67a Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Thu, 1 Oct 2020 16:16:27 +0800 Subject: [PATCH 046/156] Allow hspec-wai 0.11 (#1343) Builds fine and all tests pass. --- servant-server/servant-server.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 74eeb3ad..86cfbe8d 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -161,7 +161,7 @@ test-suite spec aeson >= 1.4.1.0 && < 1.6 , directory >= 1.3.0.0 && < 1.4 , hspec >= 2.6.0 && < 2.8 - , hspec-wai >= 0.10.1 && < 0.11 + , hspec-wai >= 0.10.1 && < 0.12 , QuickCheck >= 2.12.6.1 && < 2.14 , should-not-typecheck >= 2.1.0 && < 2.2 , temporary >= 1.3 && < 1.4 From 83bbc6d5208d2a121490624551e32af83536d1e2 Mon Sep 17 00:00:00 2001 From: Cary Robbins Date: Thu, 1 Oct 2020 07:02:43 -0500 Subject: [PATCH 047/156] Add instance for ToSample NonEmpty (#1330) --- servant-docs/src/Servant/Docs/Internal.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index b289e0c7..e9e95692 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -1064,6 +1064,7 @@ instance (ToSample a, ToSample b, ToSample c, ToSample d, ToSample e, ToSample f instance ToSample a => ToSample (Maybe a) instance (ToSample a, ToSample b) => ToSample (Either a b) instance ToSample a => ToSample [a] +instance ToSample a => ToSample (NonEmpty a) -- ToSample instances for Control.Applicative types instance ToSample a => ToSample (Const a b) From 0c0fe5b9d32050d42e288d869621fb50d912f927 Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Mon, 12 Oct 2020 13:28:35 +0200 Subject: [PATCH 048/156] Loosen bound on base16-bytestring (#1348) --- servant-client-core/servant-client-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index bb684c29..e442b304 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -77,7 +77,7 @@ library build-depends: aeson >= 1.4.1.0 && < 1.6 , base-compat >= 0.10.5 && < 0.12 - , base64-bytestring >= 1.0.0.1 && < 1.1 + , base64-bytestring >= 1.0.0.1 && < 1.2 , exceptions >= 0.10.0 && < 0.11 , free >= 5.1 && < 5.2 , http-media >= 0.7.1.3 && < 0.9 From 6a66ca6d65d2b5897da901163a789bdf11033f42 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Wed, 14 Oct 2020 10:43:57 +0200 Subject: [PATCH 049/156] Update FUNDING.yml I have a Github Sponsors account now. so people can sponsor me directly on Github. --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3fa9ad6f..784ce6fc 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -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 From 64f35430345096c5cb789b28a6fb6a174af4aec3 Mon Sep 17 00:00:00 2001 From: fisx Date: Sun, 25 Oct 2020 14:24:06 +0100 Subject: [PATCH 050/156] bump "tested-with" ghc versions. (#1350) --- .travis.yml | 311 +++++++++--------- servant-client-core/servant-client-core.cabal | 10 +- servant-client/servant-client.cabal | 8 +- servant-conduit/servant-conduit.cabal | 8 +- servant-docs/servant-docs.cabal | 8 +- servant-foreign/servant-foreign.cabal | 8 +- .../servant-http-streams.cabal | 8 +- servant-machines/servant-machines.cabal | 8 +- servant-pipes/servant-pipes.cabal | 8 +- servant-server/servant-server.cabal | 8 +- servant/servant.cabal | 10 +- 11 files changed, 171 insertions(+), 224 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9cd2a0a5..9e609a77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ # This Travis job script has been generated by a script via # -# haskell-ci '--config=cabal.haskell-ci' '--output=.travis.yml' 'cabal.project' +# haskell-ci '--branches' 'master' '--output' '.travis.yml' '--config=cabal.haskell-ci' 'cabal.project' # # To regenerate the script (for example after adjusting tested-with) run # @@ -8,7 +8,7 @@ # # For more information, see https://github.com/haskell-CI/haskell-ci # -# version: 0.10.1 +# version: 0.10.3 # version: ~> 1.0 language: c @@ -20,6 +20,7 @@ git: branches: only: - master + - master addons: google: stable cache: @@ -41,9 +42,15 @@ jobs: - 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.10.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.10.2","cabal-install-3.2"]}} + os: linux - compiler: ghc-8.10.1 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.10.1","cabal-install-3.2"]}} os: linux + - compiler: ghc-8.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"}],"packages":["ghc-8.8.4","cabal-install-3.2"]}} + os: linux - compiler: ghc-8.8.3 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.3","cabal-install-3.2"]}} os: linux @@ -114,82 +121,82 @@ install: - cat $CABALHOME/config - rm -fv cabal.project cabal.project.local cabal.project.freeze - travis_retry ${CABAL} v2-update -v - - if ! $GHCJS ; then ${CABAL} v2-install $WITHCOMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.16.3' ; fi + - if ! $GHCJS ; then ${CABAL} v2-install $WITHCOMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.17' ; fi - if $GHCJS ; then ${CABAL} v2-install -w ghc-8.4.4 --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' ; fi - if $GHCJS ; then ${CABAL} v2-install -w ghc-8.4.4 --ignore-project 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/custom-errors" >> 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 - - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client-core' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-http-streams' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-docs' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-foreign' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-server' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package tutorial' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-machines' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-conduit' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-pipes' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-file-upload' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-generic' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-client" >> cabal.project ; fi + if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-client-core" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-http-streams" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-docs" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-foreign" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-server" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/tutorial" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-machines" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-conduit" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-pipes" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/basic-auth" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/curl-mock" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/custom-errors" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/basic-streaming" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/db-postgres-pool" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/file-upload" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/generic" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/pagination" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/testing" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/structuring-apis" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-custom-monad" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-free-client" >> cabal.project ; fi + - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client-core' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-http-streams' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-docs' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-foreign' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-server' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package tutorial' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-machines' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-conduit' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-pipes' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-file-upload' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-generic' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-pagination' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - | echo "constraints: foundation >=0.0.14" >> cabal.project echo "constraints: memory <0.14.12 || >0.14.12" >> cabal.project @@ -271,75 +278,75 @@ script: - 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_custom_errors}" >> 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 - - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-client-core' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-http-streams' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-docs' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-foreign' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-server' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package tutorial' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-machines' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-conduit' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo 'package servant-pipes' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-file-upload' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-generic' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-pagination' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-testing' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi - - "if ! $GHCJS && [ $HCNUMVER -ge 80400 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_client}" >> cabal.project ; fi + if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_client_core}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_http_streams}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_docs}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_foreign}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_server}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_tutorial}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_machines}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_conduit}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_pipes}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_basic_auth}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_curl_mock}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_custom_errors}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_basic_streaming}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_db_postgres_pool}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_file_upload}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_generic}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_pagination}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_testing}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_structuring_apis}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_custom_monad}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_free_client}" >> cabal.project ; fi + - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client-core' >> cabal.project ; fi + - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-http-streams' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-docs' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-foreign' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-server' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package tutorial' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-machines' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-conduit' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-pipes' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-file-upload' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-generic' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-pagination' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - | echo "constraints: foundation >=0.0.14" >> cabal.project echo "constraints: memory <0.14.12 || >0.14.12" >> cabal.project @@ -396,21 +403,21 @@ script: # Doctest... - echo 'Doctest...' && echo -en 'travis_fold:start:doctest\\r' - perl -i -e 'while () { 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 + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_client} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_client_core} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_http_streams} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_docs} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_foreign} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_server} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_machines} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_conduit} && doctest src) ; fi + - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; 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.10.1",["--config=cabal.haskell-ci","--output=.travis.yml","cabal.project"]) +# REGENDATA ("0.10.3",["--branches","master","--output",".travis.yml","--config=cabal.haskell-ci","cabal.project"]) # EOF diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index e442b304..591069f3 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -16,14 +16,8 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 - , GHCJS == 8.4 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 + , GHCJS == 8.4 extra-source-files: CHANGELOG.md diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index bd5fb453..d3ae978d 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -20,13 +20,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 83a504fd..98e9c3ef 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -16,13 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2018-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 0e260f35..c3d932b6 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -19,13 +19,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 322faa64..40e60438 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -21,13 +21,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2015-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 62ebffa8..535b6734 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -20,13 +20,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 79401a19..f49ea4d9 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -16,13 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2018-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 2ef5969a..9e25bfbe 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -16,13 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2018-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 86cfbe8d..8772cdc5 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -23,13 +23,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant/servant.cabal b/servant/servant.cabal index a8b37b38..3f33cff6 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -20,14 +20,8 @@ maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: - GHC ==8.0.2 - || ==8.2.2 - || ==8.4.4 - || ==8.6.5 - || ==8.8.3 - || ==8.10.1 - , GHCJS == 8.4 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 + , GHCJS == 8.4 extra-source-files: CHANGELOG.md From c1105899f47d5537c1bdc8c86bee3523bdf05282 Mon Sep 17 00:00:00 2001 From: fisx Date: Sat, 31 Oct 2020 20:45:46 +0100 Subject: [PATCH 051/156] union verbs (#1314) --- cabal.project | 5 +- doc/cookbook/uverb/UVerb.lhs | 217 ++++++++++++++++++ doc/cookbook/uverb/uverb.cabal | 33 +++ servant-client-core/servant-client-core.cabal | 1 + .../src/Servant/Client/Core.hs | 2 + .../src/Servant/Client/Core/HasClient.hs | 94 +++++++- .../src/Servant/Client/Core/Reexport.hs | 2 + .../src/Servant/Client/Core/RunClient.hs | 15 +- servant-client/servant-client.cabal | 1 + .../src/Servant/Client/Internal/HttpClient.hs | 15 +- .../Client/Internal/HttpClient/Streaming.hs | 14 +- .../test/Servant/ClientTestUtils.hs | 44 +++- servant-client/test/Servant/SuccessSpec.hs | 25 +- .../src/Servant/HttpStreams/Internal.hs | 11 +- servant-server/servant-server.cabal | 3 + servant-server/src/Servant/Server.hs | 2 + servant-server/src/Servant/Server/Internal.hs | 4 +- servant-server/src/Servant/Server/UVerb.hs | 96 ++++++++ servant-server/test/Servant/ServerSpec.hs | 99 ++++++-- servant/servant.cabal | 9 +- servant/src/Servant/API.hs | 4 + servant/src/Servant/API/ContentTypes.hs | 1 - servant/src/Servant/API/UVerb.hs | 97 ++++++++ servant/src/Servant/API/UVerb/Union.hs | 147 ++++++++++++ 24 files changed, 892 insertions(+), 49 deletions(-) create mode 100644 doc/cookbook/uverb/UVerb.lhs create mode 100644 doc/cookbook/uverb/uverb.cabal create mode 100644 servant-server/src/Servant/Server/UVerb.hs create mode 100644 servant/src/Servant/API/UVerb.hs create mode 100644 servant/src/Servant/API/UVerb/Union.hs diff --git a/cabal.project b/cabal.project index 93809864..21145d96 100644 --- a/cabal.project +++ b/cabal.project @@ -25,15 +25,16 @@ packages: 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/https -- doc/cookbook/jwt-and-basic-auth/ doc/cookbook/pagination -- doc/cookbook/sentry doc/cookbook/testing + doc/cookbook/uverb doc/cookbook/structuring-apis doc/cookbook/using-custom-monad doc/cookbook/using-free-client diff --git a/doc/cookbook/uverb/UVerb.lhs b/doc/cookbook/uverb/UVerb.lhs new file mode 100644 index 00000000..e5e9bc07 --- /dev/null +++ b/doc/cookbook/uverb/UVerb.lhs @@ -0,0 +1,217 @@ +# 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. + +## Preliminaries + +```haskell +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# 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 newtype (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, ToJSON) + +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 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 () +``` diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal new file mode 100644 index 00000000..0388365d --- /dev/null +++ b/doc/cookbook/uverb/uverb.cabal @@ -0,0 +1,33 @@ +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: BSD3 +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 + +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 + default-language: Haskell2010 + ghc-options: -Wall -pgmL markdown-unlit + build-tool-depends: markdown-unlit:markdown-unlit diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 591069f3..7c3927cb 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -78,6 +78,7 @@ library , http-types >= 0.12.2 && < 0.13 , network-uri >= 2.6.1.0 && < 2.7 , safe >= 0.3.17 && < 0.4 + , sop-core >= 0.4.0.0 && < 0.6 hs-source-dirs: src default-language: Haskell2010 diff --git a/servant-client-core/src/Servant/Client/Core.hs b/servant-client-core/src/Servant/Client/Core.hs index f18d327f..cfebbb5f 100644 --- a/servant-client-core/src/Servant/Client/Core.hs +++ b/servant-client-core/src/Servant/Client/Core.hs @@ -18,6 +18,8 @@ module Servant.Client.Core -- * Client generation clientIn , HasClient(..) + , foldMapUnion + , matchUnion -- * Request , Request diff --git a/servant-client-core/src/Servant/Client/Core/HasClient.hs b/servant-client-core/src/Servant/Client/Core/HasClient.hs index 78307244..d515d120 100644 --- a/servant-client-core/src/Servant/Client/Core/HasClient.hs +++ b/servant-client-core/src/Servant/Client/Core/HasClient.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -7,6 +8,7 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} @@ -15,31 +17,48 @@ module Servant.Client.Core.HasClient ( clientIn, HasClient (..), EmptyClient (..), + foldMapUnion, + matchUnion, ) where import Prelude () import Prelude.Compat +import Control.Arrow + (left, (+++)) import Control.Monad (unless) import qualified Data.ByteString.Lazy as BL +import Data.Either + (partitionEithers) import Data.Foldable (toList) import Data.List (foldl') -import Data.Proxy - (Proxy (Proxy)) import Data.Sequence (fromList) import qualified Data.Text as T import Network.HTTP.Media (MediaType, matches, parseAccept, (//)) +import qualified Data.Sequence as Seq +import Data.SOP.BasicFunctors + (I (I), (:.:) (Comp)) +import Data.SOP.Constraint + (All) +import Data.SOP.NP + (NP (..), cpure_NP) +import Data.SOP.NS + (NS (S)) import Data.String (fromString) import Data.Text (Text, pack) +import Data.Proxy + (Proxy (Proxy)) import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.HTTP.Types + (Status) import qualified Network.HTTP.Types as H import Servant.API ((:<|>) ((:<|>)), (:>), AuthProtect, BasicAuth, BasicAuthData, @@ -54,9 +73,11 @@ import Servant.API contentType, getHeadersHList, getResponse, toQueryParam, toUrlPiece) import Servant.API.ContentTypes - (contentTypes) + (contentTypes, AllMime (allMime), AllMimeUnrender (allMimeUnrender)) import Servant.API.Modifiers (FoldRequired, RequiredArgument, foldRequiredArgument) +import Servant.API.UVerb + (HasStatus, HasStatuses (Statuses, statuses), UVerb, Union, Unique, inject, statusOf, foldMapUnion, matchUnion) import Servant.Client.Core.Auth import Servant.Client.Core.BasicAuth @@ -288,6 +309,71 @@ instance {-# OVERLAPPING #-} hoistClientMonad _ _ f ma = f ma +data ClientParseError = ClientParseError MediaType String | ClientStatusMismatch | ClientNoMatchingStatus + deriving (Eq, Show) + +instance {-# OVERLAPPING #-} + ( RunClient m, + contentTypes ~ (contentType ': otherContentTypes), + -- ('otherContentTypes' should be '_', but even -XPartialTypeSignatures does not seem + -- allow this in instance types as of 8.8.3.) + as ~ (a ': as'), + AllMime contentTypes, + ReflectMethod method, + All (AllMimeUnrender contentTypes) as, + All HasStatus as, HasStatuses as', + Unique (Statuses as) + ) => + HasClient m (UVerb method contentTypes as) + where + type Client m (UVerb method contentTypes as) = m (Union as) + + clientWithRoute _ _ request = do + let accept = Seq.fromList . allMime $ Proxy @contentTypes + -- offering to accept all mime types listed in the api gives best compatibility. eg., + -- we might not own the server implementation, and the server may choose to support + -- only part of the api. + + method = reflectMethod $ Proxy @method + acceptStatus = statuses (Proxy @as) + response <- runRequestAcceptStatus (Just acceptStatus) request {requestMethod = method, requestAccept = accept} + responseContentType <- checkContentTypeHeader response + unless (any (matches responseContentType) accept) $ do + throwClientError $ UnsupportedContentType responseContentType response + + let status = responseStatusCode response + body = responseBody response + res = tryParsers status $ mimeUnrenders (Proxy @contentTypes) body + case res of + Left errors -> throwClientError $ DecodeFailure (T.pack (show errors)) response + Right x -> return x + where + -- | Given a list of parsers of 'mkres', returns the first one that succeeds and all the + -- failures it encountered along the way + -- TODO; better name, rewrite haddocs. + tryParsers :: forall xs. All HasStatus xs => Status -> NP ([] :.: Either (MediaType, String)) xs -> Either [ClientParseError] (Union xs) + tryParsers _ Nil = Left [ClientNoMatchingStatus] + tryParsers status (Comp x :* xs) + | status == statusOf (Comp x) = + case partitionEithers x of + (err', []) -> (map (uncurry ClientParseError) err' ++) +++ S $ tryParsers status xs + (_, (res : _)) -> Right . inject . I $ res + | otherwise = -- no reason to parse in the first place. This ain't the one we're looking for + (ClientStatusMismatch :) +++ S $ tryParsers status xs + + -- | Given a list of types, parses the given response body as each type + mimeUnrenders :: + forall cts xs. + All (AllMimeUnrender cts) xs => + Proxy cts -> + BL.ByteString -> + NP ([] :.: Either (MediaType, String)) xs + mimeUnrenders ctp body = cpure_NP + (Proxy @(AllMimeUnrender cts)) + (Comp . map (\(mediaType, parser) -> left ((,) mediaType) (parser body)) . allMimeUnrender $ ctp) + + hoistClientMonad _ _ nt s = nt s + instance {-# OVERLAPPABLE #-} ( RunStreamingClient m, MimeUnrender ct chunk, ReflectMethod method, FramingUnrender framing, FromSourceIO chunk a @@ -710,4 +796,4 @@ decodedAs response ct = do Left err -> throwClientError $ DecodeFailure (T.pack err) response Right val -> return val where - accept = toList $ contentTypes ct + accept = toList $ contentTypes ct diff --git a/servant-client-core/src/Servant/Client/Core/Reexport.hs b/servant-client-core/src/Servant/Client/Core/Reexport.hs index 81e5d432..7d2aa980 100644 --- a/servant-client-core/src/Servant/Client/Core/Reexport.hs +++ b/servant-client-core/src/Servant/Client/Core/Reexport.hs @@ -5,6 +5,8 @@ module Servant.Client.Core.Reexport ( -- * HasClient HasClient(..) + , foldMapUnion + , matchUnion -- * Response (for @Raw@) , Response diff --git a/servant-client-core/src/Servant/Client/Core/RunClient.hs b/servant-client-core/src/Servant/Client/Core/RunClient.hs index fb5eb957..4487f05c 100644 --- a/servant-client-core/src/Servant/Client/Core/RunClient.hs +++ b/servant-client-core/src/Servant/Client/Core/RunClient.hs @@ -7,6 +7,7 @@ -- | Types for possible backends to run client-side `Request` queries module Servant.Client.Core.RunClient ( RunClient (..), + runRequest, RunStreamingClient (..), ClientF (..), ) where @@ -14,6 +15,8 @@ module Servant.Client.Core.RunClient ( import Prelude () import Prelude.Compat +import Network.HTTP.Types.Status + (Status) import Control.Monad.Free (Free (..), liftF) @@ -22,10 +25,15 @@ import Servant.Client.Core.Request import Servant.Client.Core.Response class Monad m => RunClient m where - -- | How to make a request. - runRequest :: Request -> m Response + -- | How to make a request, with an optional list of status codes to not throw exceptions + -- for (default: [200..299]). + runRequestAcceptStatus :: Maybe [Status] -> Request -> m Response throwClientError :: ClientError -> m a +-- | How to make a request. +runRequest :: RunClient m => Request -> m Response +runRequest = runRequestAcceptStatus Nothing + class RunClient m => RunStreamingClient m where withStreamingRequest :: Request -> (StreamingResponse -> IO a) -> m a @@ -41,6 +49,7 @@ data ClientF a | Throw ClientError deriving (Functor) +-- TODO: honour the accept-status argument. instance ClientF ~ f => RunClient (Free f) where - runRequest req = liftF (RunRequest req id) + runRequestAcceptStatus _ req = liftF (RunRequest req id) throwClientError = liftF . Throw diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index d3ae978d..ec571a92 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -108,6 +108,7 @@ test-suite spec , kan-extensions , servant-client , servant-client-core + , sop-core , stm , text , transformers diff --git a/servant-client/src/Servant/Client/Internal/HttpClient.hs b/servant-client/src/Servant/Client/Internal/HttpClient.hs index c25c8a93..29c209f8 100644 --- a/servant-client/src/Servant/Client/Internal/HttpClient.hs +++ b/servant-client/src/Servant/Client/Internal/HttpClient.hs @@ -65,7 +65,7 @@ import GHC.Generics import Network.HTTP.Media (renderHeader) import Network.HTTP.Types - (hContentType, renderQuery, statusCode) + (hContentType, renderQuery, statusCode, Status) import Servant.Client.Core import qualified Network.HTTP.Client as Client @@ -155,14 +155,14 @@ instance Alt ClientM where a b = a `catchError` \_ -> b instance RunClient ClientM where - runRequest = performRequest + runRequestAcceptStatus = performRequest throwClientError = throwError runClientM :: ClientM a -> ClientEnv -> IO (Either ClientError a) runClientM cm env = runExceptT $ flip runReaderT env $ unClientM cm -performRequest :: Request -> ClientM Response -performRequest req = do +performRequest :: Maybe [Status] -> Request -> ClientM Response +performRequest acceptStatus req = do ClientEnv m burl cookieJar' createClientRequest <- ask let clientRequest = createClientRequest burl req request <- case cookieJar' of @@ -183,8 +183,11 @@ performRequest req = do let status = Client.responseStatus response status_code = statusCode status ourResponse = clientResponseToResponse id response - unless (status_code >= 200 && status_code < 300) $ - throwError $ mkFailureResponse burl req ourResponse + goodStatus = case acceptStatus of + Nothing -> status_code >= 200 && status_code < 300 + Just good -> status `elem` good + unless goodStatus $ do + throwError $ mkFailureResponse burl req ourResponse return ourResponse where requestWithoutCookieJar :: Client.Manager -> Client.Request -> ClientM (Client.Response BSL.ByteString) diff --git a/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs b/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs index 21658638..6a1b235d 100644 --- a/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs +++ b/servant-client/src/Servant/Client/Internal/HttpClient/Streaming.hs @@ -47,7 +47,7 @@ import Data.Time.Clock (getCurrentTime) import GHC.Generics import Network.HTTP.Types - (statusCode) + (Status, statusCode) import qualified Network.HTTP.Client as Client @@ -112,7 +112,7 @@ instance Alt ClientM where a b = a `catchError` \_ -> b instance RunClient ClientM where - runRequest = performRequest + runRequestAcceptStatus = performRequest throwClientError = throwError instance RunStreamingClient ClientM where @@ -136,8 +136,8 @@ withClientM cm env k = runClientM :: NFData a => ClientM a -> ClientEnv -> IO (Either ClientError a) runClientM cm env = withClientM cm env (evaluate . force) -performRequest :: Request -> ClientM Response -performRequest req = do +performRequest :: Maybe [Status] -> Request -> ClientM Response +performRequest acceptStatus req = do -- TODO: should use Client.withResponse here too ClientEnv m burl cookieJar' createClientRequest <- ask let clientRequest = createClientRequest burl req @@ -165,10 +165,14 @@ performRequest req = do let status = Client.responseStatus response status_code = statusCode status ourResponse = clientResponseToResponse id response - unless (status_code >= 200 && status_code < 300) $ + goodStatus = case acceptStatus of + Nothing -> status_code >= 200 && status_code < 300 + Just good -> status `elem` good + unless goodStatus $ do throwError $ mkFailureResponse burl req ourResponse return ourResponse +-- | TODO: support UVerb ('acceptStatus' argument, like in 'performRequest' above). performWithStreamingRequest :: Request -> (StreamingResponse -> IO a) -> ClientM a performWithStreamingRequest req k = do m <- asks manager diff --git a/servant-client/test/Servant/ClientTestUtils.hs b/servant-client/test/Servant/ClientTestUtils.hs index 6f385010..0864896d 100644 --- a/servant-client/test/Servant/ClientTestUtils.hs +++ b/servant-client/test/Servant/ClientTestUtils.hs @@ -9,6 +9,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} @@ -26,10 +27,14 @@ import Control.Concurrent import Control.Monad.Error.Class (throwError) import Data.Aeson +import qualified Data.ByteString.Lazy as LazyByteString import Data.Char (chr, isPrint) import Data.Monoid () import Data.Proxy +import Data.Text (Text) +import qualified Data.Text as Text +import Data.Text.Encoding (encodeUtf8, decodeUtf8) import GHC.Generics (Generic) import qualified Network.HTTP.Client as C @@ -47,8 +52,11 @@ import Servant.API ((:<|>) ((:<|>)), (:>), AuthProtect, BasicAuth, BasicAuthData (..), Capture, CaptureAll, DeleteNoContent, EmptyAPI, FormUrlEncoded, Get, Header, - Headers, JSON, NoContent (NoContent), Post, QueryFlag, - QueryParam, QueryParams, Raw, ReqBody, addHeader) + Headers, JSON, MimeRender(mimeRender), + MimeUnrender(mimeUnrender), NoContent (NoContent), PlainText, + Post, QueryFlag, QueryParam, QueryParams, Raw, ReqBody, + StdMethod(GET), Union, UVerb, WithStatus(WithStatus), + addHeader) import Servant.Client import qualified Servant.Client.Core.Auth as Auth import Servant.Server @@ -63,7 +71,7 @@ _ = client comprehensiveAPIWithoutStreaming data Person = Person { _name :: String , _age :: Integer - } deriving (Eq, Show, Generic) + } deriving (Eq, Show, Read, Generic) instance ToJSON Person instance FromJSON Person @@ -74,6 +82,15 @@ instance FromForm Person instance Arbitrary Person where arbitrary = Person <$> arbitrary <*> arbitrary +instance MimeRender PlainText Person where + mimeRender _ = LazyByteString.fromStrict . encodeUtf8 . Text.pack . show + +instance MimeUnrender PlainText Person where + mimeUnrender _ = + -- This does not handle any errors, but it should be fine for tests + Right . read . Text.unpack . decodeUtf8 . LazyByteString.toStrict + + alice :: Person alice = Person "Alice" 42 @@ -105,6 +122,12 @@ type Api = :<|> "deleteContentType" :> DeleteNoContent :<|> "redirectWithCookie" :> Raw :<|> "empty" :> EmptyAPI + :<|> "uverb-success-or-redirect" :> + Capture "bool" Bool :> + UVerb 'GET '[PlainText] '[WithStatus 200 Person, + WithStatus 301 Text] + :<|> "uverb-get-created" :> UVerb 'GET '[PlainText] '[WithStatus 201 Person] + api :: Proxy Api api = Proxy @@ -126,6 +149,10 @@ getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] getRespHeaders :: ClientM (Headers TestHeaders Bool) getDeleteContentType :: ClientM NoContent getRedirectWithCookie :: HTTP.Method -> ClientM Response +uverbGetSuccessOrRedirect :: Bool + -> ClientM (Union '[WithStatus 200 Person, + WithStatus 301 Text]) +uverbGetCreated :: ClientM (Union '[WithStatus 201 Person]) getRoot :<|> getGet @@ -143,7 +170,9 @@ getRoot :<|> getRespHeaders :<|> getDeleteContentType :<|> getRedirectWithCookie - :<|> EmptyClient = client api + :<|> EmptyClient + :<|> uverbGetSuccessOrRedirect + :<|> uverbGetCreated = client api server :: Application server = serve api ( @@ -166,7 +195,12 @@ server = serve api ( :<|> (return $ addHeader 1729 $ addHeader "eg2" True) :<|> return NoContent :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.found302 [("Location", "testlocation"), ("Set-Cookie", "testcookie=test")] "") - :<|> emptyServer) + :<|> emptyServer + :<|> (\shouldRedirect -> if shouldRedirect + then respond (WithStatus @301 ("redirecting" :: Text)) + else respond (WithStatus @200 alice )) + :<|> respond (WithStatus @201 carol) + ) type FailApi = "get" :> Raw diff --git a/servant-client/test/Servant/SuccessSpec.hs b/servant-client/test/Servant/SuccessSpec.hs index 272b607c..9e12f034 100644 --- a/servant-client/test/Servant/SuccessSpec.hs +++ b/servant-client/test/Servant/SuccessSpec.hs @@ -8,6 +8,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} @@ -31,6 +32,8 @@ import Data.Foldable import Data.Maybe (listToMaybe) import Data.Monoid () +import Data.Text + (Text) import qualified Network.HTTP.Client as C import qualified Network.HTTP.Types as HTTP import Test.Hspec @@ -39,7 +42,7 @@ import Test.HUnit import Test.QuickCheck import Servant.API - (NoContent (NoContent), getHeaders) + (NoContent (NoContent), WithStatus(WithStatus), getHeaders) import Servant.Client import qualified Servant.Client.Core.Request as Req import Servant.Client.Internal.HttpClient (defaultMakeClientRequest) @@ -151,3 +154,23 @@ successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do result <- left show <$> runClient (getMultiple cap num flag body) baseUrl return $ result === Right (cap, num, flag, body) + + context "With a route that can either return success or redirect" $ do + it "Redirects when appropriate" $ \(_, baseUrl) -> do + eitherResponse <- runClient (uverbGetSuccessOrRedirect True) baseUrl + case eitherResponse of + Left clientError -> fail $ show clientError + Right response -> matchUnion response `shouldBe` Just (WithStatus @301 @Text "redirecting") + + it "Returns a proper response when appropriate" $ \(_, baseUrl) -> do + eitherResponse <- runClient (uverbGetSuccessOrRedirect False) baseUrl + case eitherResponse of + Left clientError -> fail $ show clientError + Right response -> matchUnion response `shouldBe` Just (WithStatus @200 alice) + + context "with a route that uses uverb but only has a single response" $ + it "returns the expected response" $ \(_, baseUrl) -> do + eitherResponse <- runClient (uverbGetCreated) baseUrl + case eitherResponse of + Left clientError -> fail $ show clientError + Right response -> matchUnion response `shouldBe` Just (WithStatus @201 carol) diff --git a/servant-http-streams/src/Servant/HttpStreams/Internal.hs b/servant-http-streams/src/Servant/HttpStreams/Internal.hs index 54c920bc..4d2bae2e 100644 --- a/servant-http-streams/src/Servant/HttpStreams/Internal.hs +++ b/servant-http-streams/src/Servant/HttpStreams/Internal.hs @@ -141,7 +141,7 @@ instance Alt ClientM where a b = a `catchError` \_ -> b instance RunClient ClientM where - runRequest = performRequest + runRequestAcceptStatus = performRequest throwClientError = throwError instance RunStreamingClient ClientM where @@ -155,8 +155,8 @@ withClientM cm env k = let Codensity f = runExceptT $ flip runReaderT env $ unClientM cm in f k -performRequest :: Request -> ClientM Response -performRequest req = do +performRequest :: Maybe [Status] -> Request -> ClientM Response +performRequest acceptStatus req = do ClientEnv burl conn <- ask let (req', body) = requestToClientRequest burl req x <- ClientM $ lift $ lift $ Codensity $ \k -> do @@ -165,7 +165,10 @@ performRequest req = do let sc = Client.getStatusCode res' lbs <- BSL.fromChunks <$> Streams.toList body' let res'' = clientResponseToResponse res' lbs - if sc >= 200 && sc < 300 + goodStatus = case acceptStatus of + Nothing -> sc >= 200 && sc < 300 + Just good -> sc `elem` (statusCode <$> good) + if goodStatus then k (Right res'') else k (Left (mkFailureResponse burl req res'')) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 8772cdc5..e417a6eb 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -51,6 +51,7 @@ library Servant.Server.Internal.RoutingApplication Servant.Server.Internal.ServerError Servant.Server.StaticFiles + Servant.Server.UVerb -- deprecated exposed-modules: @@ -84,6 +85,7 @@ library , network-uri >= 2.6.1.0 && < 2.8 , monad-control >= 1.0.2.3 && < 1.1 , network >= 2.8 && < 3.2 + , sop-core >= 0.4.0.0 && < 0.6 , string-conversions >= 0.4.0.1 && < 0.5 , resourcet >= 1.2.2 && < 1.3 , tagged >= 0.8.6 && < 0.9 @@ -94,6 +96,7 @@ library hs-source-dirs: src default-language: Haskell2010 + ghc-options: -Wall -Wno-redundant-constraints executable greet diff --git a/servant-server/src/Servant/Server.hs b/servant-server/src/Servant/Server.hs index 99c0b1e5..5d40eb6f 100644 --- a/servant-server/src/Servant/Server.hs +++ b/servant-server/src/Servant/Server.hs @@ -110,6 +110,7 @@ module Servant.Server -- * Re-exports , Application , Tagged (..) + , module Servant.Server.UVerb ) where @@ -122,6 +123,7 @@ import Data.Text import Network.Wai (Application) import Servant.Server.Internal +import Servant.Server.UVerb -- * Implementing Servers diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index a234f145..ba42c8e9 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -65,7 +65,7 @@ import Network.Socket (SockAddr) import Network.Wai (Application, Request, httpVersion, isSecure, lazyRequestBody, - queryString, remoteHost, requestBody, requestHeaders, + queryString, remoteHost, getRequestBodyChunk, requestHeaders, requestMethod, responseLBS, responseStream, vault) import Prelude () import Prelude.Compat @@ -681,7 +681,7 @@ instance bodyCheck fromRS = withRequest $ \req -> do let mimeUnrender' = mimeUnrender (Proxy :: Proxy ctype) :: BL.ByteString -> Either String chunk let framingUnrender' = framingUnrender (Proxy :: Proxy framing) mimeUnrender' :: SourceIO B.ByteString -> SourceIO chunk - let body = requestBody req + let body = getRequestBodyChunk req let rs = S.fromAction B.null body let rs' = fromRS $ framingUnrender' rs return rs' diff --git a/servant-server/src/Servant/Server/UVerb.hs b/servant-server/src/Servant/Server/UVerb.hs new file mode 100644 index 00000000..bbddba34 --- /dev/null +++ b/servant-server/src/Servant/Server/UVerb.hs @@ -0,0 +1,96 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} + +{-# OPTIONS_GHC -Wno-orphans #-} +{-# OPTIONS_GHC -Wno-redundant-constraints #-} + +module Servant.Server.UVerb + ( respond, + IsServerResource, + ) +where + +import Data.Proxy (Proxy (Proxy)) +import Data.SOP (I (I)) +import Data.SOP.Constraint (All, And) +import Data.String.Conversions (LBS, cs) +import Network.HTTP.Types (Status, hContentType) +import Network.Wai (responseLBS) +import Servant.API (ReflectMethod, reflectMethod) +import Servant.API.ContentTypes (AllCTRender (handleAcceptH), AllMime) +import Servant.API.UVerb (HasStatus, IsMember, Statuses, UVerb, Union, Unique, foldMapUnion, inject, statusOf) +import Servant.Server.Internal (Context, Delayed, Handler, HasServer (..), RouteResult (FailFatal, Route), Router, Server, ServerT, acceptCheck, addAcceptCheck, addMethodCheck, allowedMethodHead, err406, getAcceptHeader, leafRouter, methodCheck, runAction) + + +-- | 'return' for 'UVerb' handlers. Takes a value of any of the members of the open union, +-- and will construct a union value in an 'Applicative' (eg. 'Server'). +respond :: + forall (x :: *) (xs :: [*]) (f :: * -> *). + (Applicative f, HasStatus x, IsMember x xs) => + x -> + f (Union xs) +respond = pure . inject . I + +-- | Helper constraint used in @instance 'HasServer' 'UVerb'@. +type IsServerResource contentTypes = AllCTRender contentTypes `And` HasStatus + +instance + ( ReflectMethod method, + AllMime contentTypes, + All (IsServerResource contentTypes) as, + Unique (Statuses as) -- for consistency with servant-swagger (server would work fine + -- wihtout; client is a bit of a corner case, because it dispatches + -- the parser based on the status code. with this uniqueness + -- constraint it won't have to run more than one parser in weird + -- corner cases. + ) => + HasServer (UVerb method contentTypes as) context + where + type ServerT (UVerb method contentTypes as) m = m (Union as) + + hoistServerWithContext _ _ nt s = nt s + + route :: + forall env. + Proxy (UVerb method contentTypes as) -> + Context context -> + Delayed env (Server (UVerb method contentTypes as)) -> + Router env + route _proxy _ctx action = leafRouter route' + where + method = reflectMethod (Proxy @method) + route' env request cont = do + let action' :: Delayed env (Handler (Union as)) + action' = + action + `addMethodCheck` methodCheck method request + `addAcceptCheck` acceptCheck (Proxy @contentTypes) (getAcceptHeader request) + mkProxy :: a -> Proxy a + mkProxy _ = Proxy + + runAction action' env request cont $ \(output :: Union as) -> do + let encodeResource :: (AllCTRender contentTypes a, HasStatus a) => a -> (Status, Maybe (LBS, LBS)) + encodeResource res = + ( statusOf $ mkProxy res, + handleAcceptH (Proxy @contentTypes) (getAcceptHeader request) res + ) + pickResource :: Union as -> (Status, Maybe (LBS, LBS)) + pickResource = foldMapUnion (Proxy @(IsServerResource contentTypes)) encodeResource + + case pickResource output of + (_, Nothing) -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does + (status, Just (contentT, body)) -> + let bdy = if allowedMethodHead method request then "" else body + in Route $ responseLBS status ((hContentType, cs contentT) : []) bdy diff --git a/servant-server/test/Servant/ServerSpec.hs b/servant-server/test/Servant/ServerSpec.hs index 90e72667..87c84421 100644 --- a/servant-server/test/Servant/ServerSpec.hs +++ b/servant-server/test/Servant/ServerSpec.hs @@ -7,7 +7,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -{-# LANGUAGE TypeSynonymInstances #-} {-# OPTIONS_GHC -freduction-depth=100 #-} module Servant.ServerSpec where @@ -49,14 +48,16 @@ import Network.Wai.Test import Servant.API ((:<|>) (..), (:>), AuthProtect, BasicAuth, BasicAuthData (BasicAuthData), Capture, Capture', CaptureAll, - Delete, EmptyAPI, Get, Header, Headers, HttpVersion, - IsSecure (..), JSON, Lenient, NoContent (..), NoContentVerb, - NoFraming, OctetStream, Patch, PlainText, Post, Put, - QueryFlag, QueryParam, QueryParams, Raw, RemoteHost, ReqBody, - SourceIO, StdMethod (..), Stream, Strict, Verb, addHeader) + Delete, EmptyAPI, Get, HasStatus(StatusOf), Header, Headers, + HttpVersion, IsSecure (..), JSON, Lenient, NoContent (..), + NoContentVerb, NoFraming, OctetStream, Patch, PlainText, Post, + Put, QueryFlag, QueryParam, QueryParams, Raw, RemoteHost, + ReqBody, SourceIO, StdMethod (..), Stream, Strict, Union, + UVerb, Verb, addHeader) import Servant.Server (Context ((:.), EmptyContext), Handler, Server, Tagged (..), - emptyServer, err401, err403, err404, serve, serveWithContext) + emptyServer, err401, err403, err404, respond, serve, + serveWithContext) import Servant.Test.ComprehensiveAPI import qualified Servant.Types.SourceT as S import Test.Hspec @@ -87,6 +88,7 @@ comprehensiveApiContext = NamedContext EmptyContext :. EmptyContext spec :: Spec spec = do verbSpec + uverbSpec captureSpec captureAllSpec queryParamSpec @@ -253,8 +255,8 @@ captureSpec = do with (return (serve (Proxy :: Proxy (Capture "captured" String :> Raw)) - (\ "captured" -> Tagged $ \request_ respond -> - respond $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do + (\ "captured" -> Tagged $ \request_ sendResponse -> + sendResponse $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do it "strips the captured path snippet from pathInfo" $ do get "/captured/foo" `shouldRespondWith` (fromString (show ["foo" :: String])) @@ -305,8 +307,8 @@ captureAllSpec = do with (return (serve (Proxy :: Proxy (CaptureAll "segments" String :> Raw)) - (\ _captured -> Tagged $ \request_ respond -> - respond $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do + (\ _captured -> Tagged $ \request_ sendResponse -> + sendResponse $ responseLBS ok200 [] (cs $ show $ pathInfo request_)))) $ do it "consumes everything from pathInfo" $ do get "/captured/foo/bar/baz" `shouldRespondWith` (fromString (show ([] :: [Int]))) @@ -544,8 +546,8 @@ rawApi :: Proxy RawApi rawApi = Proxy rawApplication :: Show a => (Request -> a) -> Tagged m Application -rawApplication f = Tagged $ \request_ respond -> - respond $ responseLBS ok200 [] +rawApplication f = Tagged $ \request_ sendResponse -> + sendResponse $ responseLBS ok200 [] (cs $ show $ f request_) rawSpec :: Spec @@ -706,7 +708,7 @@ basicAuthApi = Proxy basicAuthServer :: Server BasicAuthAPI basicAuthServer = const (return jerry) :<|> - (Tagged $ \ _ respond -> respond $ responseLBS imATeapot418 [] "") + (Tagged $ \ _ sendResponse -> sendResponse $ responseLBS imATeapot418 [] "") basicAuthContext :: Context '[ BasicAuthCheck () ] basicAuthContext = @@ -751,7 +753,7 @@ genAuthApi = Proxy genAuthServer :: Server GenAuthAPI genAuthServer = const (return tweety) - :<|> (Tagged $ \ _ respond -> respond $ responseLBS imATeapot418 [] "") + :<|> (Tagged $ \ _ sendResponse -> sendResponse $ responseLBS imATeapot418 [] "") type instance AuthServerData (AuthProtect "auth") = () @@ -781,6 +783,73 @@ genAuthSpec = do it "plays nice with subsequent Raw endpoints" $ do get "/foo" `shouldRespondWith` 418 +-- }}} +------------------------------------------------------------------------------ +-- * UVerb {{{ +------------------------------------------------------------------------------ + +newtype PersonResponse = PersonResponse Person + deriving Generic +instance ToJSON PersonResponse +instance HasStatus PersonResponse where + type StatusOf PersonResponse = 200 + +newtype RedirectResponse = RedirectResponse String + deriving Generic +instance ToJSON RedirectResponse +instance HasStatus RedirectResponse where + type StatusOf RedirectResponse = 301 + +newtype AnimalResponse = AnimalResponse Animal + deriving Generic +instance ToJSON AnimalResponse +instance HasStatus AnimalResponse where + type StatusOf AnimalResponse = 203 + + +type UVerbApi + = "person" :> Capture "shouldRedirect" Bool :> UVerb 'GET '[JSON] '[PersonResponse, RedirectResponse] + :<|> "animal" :> UVerb 'GET '[JSON] '[AnimalResponse] + +uverbSpec :: Spec +uverbSpec = describe "Servant.API.UVerb " $ do + let + joe = Person "joe" 42 + mouse = Animal "Mouse" 7 + + personHandler + :: Bool + -> Handler (Union '[PersonResponse + ,RedirectResponse]) + personHandler True = respond $ RedirectResponse "over there!" + personHandler False = respond $ PersonResponse joe + + animalHandler = respond $ AnimalResponse mouse + + server :: Server UVerbApi + server = personHandler :<|> animalHandler + + with (pure $ serve (Proxy :: Proxy UVerbApi) server) $ do + context "A route returning either 301/String or 200/Person" $ do + context "when requesting the person" $ do + let theRequest = THW.get "/person/false" + it "returns status 200" $ + theRequest `shouldRespondWith` 200 + it "returns a person" $ do + response <- theRequest + liftIO $ decode' (simpleBody response) `shouldBe` Just joe + context "requesting the redirect" $ + it "returns a message and status 301" $ + THW.get "/person/true" + `shouldRespondWith` "\"over there!\"" {matchStatus = 301} + context "a route with a single response type" $ do + let theRequest = THW.get "/animal" + it "should return the defined status code" $ + theRequest `shouldRespondWith` 203 + it "should return the expected response" $ do + response <- theRequest + liftIO $ decode' (simpleBody response) `shouldBe` Just mouse + -- }}} ------------------------------------------------------------------------------ -- * Test data types {{{ diff --git a/servant/servant.cabal b/servant/servant.cabal index 3f33cff6..e02e093f 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -54,6 +54,8 @@ library Servant.API.Stream Servant.API.Sub Servant.API.TypeLevel + Servant.API.UVerb + Servant.API.UVerb.Union Servant.API.Vault Servant.API.Verbs Servant.API.WithNamedContext @@ -78,6 +80,7 @@ library base >= 4.9 && < 4.15 , bytestring >= 0.10.8.1 && < 0.11 , mtl >= 2.2.2 && < 2.3 + , sop-core >= 0.4.0.0 && < 0.6 , transformers >= 0.5.2.0 && < 0.6 , text >= 1.2.3.0 && < 1.3 @@ -108,11 +111,13 @@ library hs-source-dirs: src default-language: Haskell2010 - other-extensions: CPP + other-extensions: AllowAmbiguousTypes + , CPP , ConstraintKinds , DataKinds , DeriveDataTypeable , DeriveGeneric + , ExplicitNamespaces , FlexibleContexts , FlexibleInstances , FunctionalDependencies @@ -121,11 +126,13 @@ library , MultiParamTypeClasses , OverloadedStrings , PolyKinds + , RankNTypes , ScopedTypeVariables , TupleSections , TypeFamilies , TypeOperators , UndecidableInstances + ghc-options: -Wall -Wno-redundant-constraints test-suite spec diff --git a/servant/src/Servant/API.hs b/servant/src/Servant/API.hs index 772a3887..66b86d78 100644 --- a/servant/src/Servant/API.hs +++ b/servant/src/Servant/API.hs @@ -32,6 +32,7 @@ module Servant.API ( -- * Actual endpoints, distinguished by HTTP method module Servant.API.Verbs, + module Servant.API.UVerb, -- * Streaming endpoints, distinguished by HTTP method module Servant.API.Stream, @@ -132,6 +133,9 @@ import Servant.API.Verbs PutCreated, PutNoContent, PutNonAuthoritative, ReflectMethod (reflectMethod), StdMethod (..), Verb, NoContentVerb) +import Servant.API.UVerb + (UVerb, Union, HasStatus, StatusOf, statusOf, Statuses, + WithStatus (..), IsMember, Unique, inject) import Servant.API.WithNamedContext (WithNamedContext) import Servant.Links diff --git a/servant/src/Servant/API/ContentTypes.hs b/servant/src/Servant/API/ContentTypes.hs index 145ecfb5..d6d200ad 100644 --- a/servant/src/Servant/API/ContentTypes.hs +++ b/servant/src/Servant/API/ContentTypes.hs @@ -419,7 +419,6 @@ instance MimeUnrender OctetStream BS.ByteString where mimeUnrender _ = Right . toStrict - -- $setup -- >>> :set -XFlexibleInstances -- >>> :set -XMultiParamTypeClasses diff --git a/servant/src/Servant/API/UVerb.hs b/servant/src/Servant/API/UVerb.hs new file mode 100644 index 00000000..4074adef --- /dev/null +++ b/servant/src/Servant/API/UVerb.hs @@ -0,0 +1,97 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} + +-- | An alternative to 'Verb' for end-points that respond with a resource value of any of an +-- open union of types, and specific status codes for each type in this union. (`UVerb` is +-- short for `UnionVerb`) +-- +-- This can be used for returning (rather than throwing) exceptions in a server as in, say +-- @'[Report, WaiError]@; or responding with either a 303 forward with a location header, or +-- 201 created with a different body type, depending on the circumstances. (All of this can +-- be done with vanilla servant-server by throwing exceptions, but it can't be represented in +-- the API types without something like `UVerb`.) +-- +-- See for a working example. +module Servant.API.UVerb + ( UVerb, + HasStatus (StatusOf), + statusOf, + HasStatuses (Statuses, statuses), + WithStatus (..), + module Servant.API.UVerb.Union, + ) +where + +import Data.Aeson (FromJSON, ToJSON) +import Data.Proxy (Proxy (Proxy)) +import qualified GHC.Generics as GHC +import GHC.TypeLits (Nat) +import Network.HTTP.Types (Status, StdMethod) +import Servant.API.ContentTypes (MimeRender (mimeRender), MimeUnrender (mimeUnrender), NoContent) +import Servant.API.Status (KnownStatus, statusVal) +import Servant.API.UVerb.Union + +class KnownStatus (StatusOf a) => HasStatus (a :: *) where + type StatusOf (a :: *) :: Nat + +statusOf :: forall a proxy. HasStatus a => proxy a -> Status +statusOf = const (statusVal (Proxy :: Proxy (StatusOf a))) + +instance KnownStatus n => HasStatus (WithStatus n a) where + type StatusOf (WithStatus n a) = n + +-- | If an API can respond with 'NoContent' we assume that this will happen +-- with the status code 204 No Content. If this needs to be overridden, +-- 'WithStatus' can be used. +instance HasStatus NoContent where + type StatusOf NoContent = 204 + +class HasStatuses (as :: [*]) where + type Statuses (as :: [*]) :: [Nat] + statuses :: Proxy as -> [Status] + +instance HasStatuses '[] where + type Statuses '[] = '[] + statuses _ = [] + +instance (HasStatus a, HasStatuses as) => HasStatuses (a ': as) where + type Statuses (a ': as) = StatusOf a ': Statuses as + statuses _ = statusOf (Proxy :: Proxy a) : statuses (Proxy :: Proxy as) + +newtype WithStatus (k :: Nat) a = WithStatus a + deriving (Eq, Show, GHC.Generic) + +instance (GHC.Generic (WithStatus n a), ToJSON a) => ToJSON (WithStatus n a) + +instance (GHC.Generic (WithStatus n a), FromJSON a) => FromJSON (WithStatus n a) + +instance MimeRender ctype a => MimeRender ctype (WithStatus _status a) where + mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a + +instance MimeUnrender ctype a => MimeUnrender ctype (WithStatus _status a) where + mimeUnrender contentTypeProxy input = + WithStatus <$> mimeUnrender contentTypeProxy input + +-- | A variant of 'Verb' that can have any of a number of response values and status codes. +-- +-- FUTUREWORK: it would be nice to make 'Verb' a special case of 'UVerb', and only write +-- instances for 'HasServer' etc. for the latter, getting them for the former for free. +-- Something like: +-- +-- @type Verb method statusCode contentTypes a = UVerb method contentTypes [WithStatus statusCode a]@ +-- +-- Backwards compatibility is tricky, though: this type alias would mean people would have to +-- use 'respond' instead of 'pure' or 'return', so all old handlers would have to be rewritten. +data UVerb (method :: StdMethod) (contentTypes :: [*]) (as :: [*]) diff --git a/servant/src/Servant/API/UVerb/Union.hs b/servant/src/Servant/API/UVerb/Union.hs new file mode 100644 index 00000000..1b776216 --- /dev/null +++ b/servant/src/Servant/API/UVerb/Union.hs @@ -0,0 +1,147 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ExplicitNamespaces #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} + +{- + +Copyright Dennis Gosnell (c) 2017-2018 + +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 Author name here 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. + +-} + +-- | Type-level code for implementing and using 'UVerb'. Heavily inspired by +-- [world-piece](https://github.com/cdepillabout/world-peace). +module Servant.API.UVerb.Union +( IsMember +, Unique +, Union +, inject +, eject +, foldMapUnion +, matchUnion +) +where + +import Data.Proxy (Proxy) +import Data.SOP.BasicFunctors (I, unI) +import Data.SOP.Constraint +import Data.SOP.NS +import Data.Type.Bool (If) +import Data.Type.Equality (type (==)) +import GHC.TypeLits + +type Union = NS I + +-- | Convenience function to apply a function to an unknown union element using a type class. +-- All elements of the union must have instances in the type class, and the function is +-- applied unconditionally. +-- +-- See also: 'matchUnion'. +foldMapUnion :: + forall (c :: * -> Constraint) (a :: *) (as :: [*]). + All c as => + Proxy c -> + (forall x. c x => x -> a) -> + Union as -> + a +foldMapUnion proxy go = cfoldMap_NS proxy (go . unI) + +-- | Convenience function to extract a union element using 'cast', ie. return the value if the +-- selected type happens to be the actual type of the union in this value, or 'Nothing' +-- otherwise. +-- +-- See also: 'foldMapUnion'. +matchUnion :: forall (a :: *) (as :: [*]). (IsMember a as) => Union as -> Maybe a +matchUnion = fmap unI . eject + +-- * Stuff stolen from 'Data.WorldPeace" but for generics-sop + +-- (this could to go sop-core, except it's probably too specialized to the servant use-case.) + +type IsMember (a :: u) (as :: [u]) = (Unique as, CheckElemIsMember a as, UElem a as) + +class UElem x xs where + inject :: f x -> NS f xs + eject :: NS f xs -> Maybe (f x) + +instance {-# OVERLAPPING #-} UElem x (x ': xs) where + inject = Z + eject (Z x) = Just x + eject _ = Nothing + +instance {-# OVERLAPPING #-} UElem x xs => UElem x (x' ': xs) where + inject = S . inject + eject (Z _) = Nothing + eject (S ns) = eject ns + +-- | Check whether @a@ is in list. This will throw nice errors if the element is not in the +-- list, or if there is a duplicate in the list. +type family CheckElemIsMember (a :: k) (as :: [k]) :: Constraint where + CheckElemIsMember a as = + If (Elem a as) (() :: Constraint) (TypeError (NoElementError a as)) + +type NoElementError (r :: k) (rs :: [k]) = + 'Text "Expected one of:" + ':$$: 'Text " " ':<>: 'ShowType rs + ':$$: 'Text "But got:" + ':$$: 'Text " " ':<>: 'ShowType r + +type DuplicateElementError (rs :: [k]) = + 'Text "Duplicate element in list:" + ':$$: 'Text " " ':<>: 'ShowType rs + +type family Elem (x :: k) (xs :: [k]) :: Bool where + Elem _ '[] = 'False + Elem x (x' ': xs) = + If (x == x') 'True (Elem x xs) + +type family Unique xs :: Constraint where + Unique xs = If (Nubbed xs == 'True) (() :: Constraint) (TypeError (DuplicateElementError xs)) + +type family Nubbed xs :: Bool where + Nubbed '[] = 'True + Nubbed (x ': xs) = If (Elem x xs) 'False (Nubbed xs) + +_testNubbed :: ( ( Nubbed '[Bool, Int, Int] ~ 'False + , Nubbed '[Int, Int, Bool] ~ 'False + , Nubbed '[Int, Bool] ~ 'True + ) + => a) -> a +_testNubbed = id From 9e4a97eb78cf4c26fd7e176264c78a7a348e7363 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Wed, 4 Nov 2020 15:11:15 +0300 Subject: [PATCH 052/156] Loosen upper bound on wai-extra --- servant-server/servant-server.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index e417a6eb..25a71679 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -162,7 +162,7 @@ test-suite spec , QuickCheck >= 2.12.6.1 && < 2.14 , should-not-typecheck >= 2.1.0 && < 2.2 , temporary >= 1.3 && < 1.4 - , wai-extra >= 3.0.24.3 && < 3.1 + , wai-extra >= 3.0.24.3 && < 3.2 build-tool-depends: hspec-discover:hspec-discover >= 2.6.0 && <2.8 From bd698cad3b00f134c7a7ba8b6ea0fd244044d6cb Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Wed, 4 Nov 2020 15:28:20 +0300 Subject: [PATCH 053/156] Bump version in preparation for new release --- servant-client-core/CHANGELOG.md | 12 ++++++++++++ servant-client-core/servant-client-core.cabal | 2 +- servant-client/CHANGELOG.md | 11 +++++++++++ servant-client/servant-client.cabal | 4 ++-- servant-docs/CHANGELOG.md | 12 ++++++++++++ servant-docs/servant-docs.cabal | 2 +- servant-http-streams/CHANGELOG.md | 11 +++++++++++ servant-http-streams/servant-http-streams.cabal | 2 +- servant-server/CHANGELOG.md | 12 ++++++++++++ servant-server/servant-server.cabal | 2 +- servant/CHANGELOG.md | 12 ++++++++++++ servant/servant.cabal | 2 +- 12 files changed, 77 insertions(+), 7 deletions(-) diff --git a/servant-client-core/CHANGELOG.md b/servant-client-core/CHANGELOG.md index e0adbef4..d1f6774f 100644 --- a/servant-client-core/CHANGELOG.md +++ b/servant-client-core/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.1 +------ + +### Significant changes + +- Union verbs + +### Other changes + +- Bump "tested-with" ghc versions +- Loosen bound on base16-bytestring + 0.18 ---- diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 7c3927cb..b491ba2d 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client-core -version: 0.18 +version: 0.18.1 synopsis: Core functionality and class for client function generation for servant APIs category: Servant, Web diff --git a/servant-client/CHANGELOG.md b/servant-client/CHANGELOG.md index 5a056ad5..b0a28654 100644 --- a/servant-client/CHANGELOG.md +++ b/servant-client/CHANGELOG.md @@ -1,6 +1,17 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.1 +------ + +### Significant changes + +- Union verbs + +### Other changes + +- Bump "tested-with" ghc versions + 0.18 ---- diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index ec571a92..69352821 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client -version: 0.18 +version: 0.18.1 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -58,7 +58,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18 && <0.18.1 + , servant-client-core >= 0.18 && <0.18.2 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-docs/CHANGELOG.md b/servant-docs/CHANGELOG.md index 10694590..be853dbe 100644 --- a/servant-docs/CHANGELOG.md +++ b/servant-docs/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-docs/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.11.7 +------ + +### Significant changes + +- Add instance for ToSample NonEmpty + +### Other changes + +- Bump "tested-with" ghc versions +- Fix servant-docs code sample in README + 0.11.5 ---- diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index c3d932b6..27f2afa6 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-docs -version: 0.11.6 +version: 0.11.7 synopsis: generate API docs for your servant webservice category: Servant, Web diff --git a/servant-http-streams/CHANGELOG.md b/servant-http-streams/CHANGELOG.md index eaef7f39..3d1e11ca 100644 --- a/servant-http-streams/CHANGELOG.md +++ b/servant-http-streams/CHANGELOG.md @@ -1,6 +1,17 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-http-streams/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.1 +------ + +### Significant changes + +- Union verbs + +### Other changes + +- Bump "tested-with" ghc versions + 0.18 ---- diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 535b6734..b3c1e2fc 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-http-streams -version: 0.18 +version: 0.18.1 synopsis: Automatic derivation of querying functions for servant category: Servant, Web diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index 44680769..172d103f 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.1 +------ + +### Significant changes + +- Union verbs + +### Other changes + +- Bump "tested-with" ghc versions +- Allow newer dependencies + 0.18 ---- diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 25a71679..5ea2d40f 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-server -version: 0.18 +version: 0.18.1 synopsis: A family of combinators for defining webservices APIs and serving them category: Servant, Web diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index c9d72a70..700afcfa 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -1,5 +1,17 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.1 +------ + +### Significant changes + +- Union verbs + +### Other changes + +- Bump "tested-with" ghc versions +- Allow newer dependencies + 0.18 ---- diff --git a/servant/servant.cabal b/servant/servant.cabal index e02e093f..79fcd5ff 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant -version: 0.18 +version: 0.18.1 synopsis: A family of combinators for defining webservices APIs category: Servant, Web From 4c72c08830d6370908f5a82ac0b7435ff6805e18 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Thu, 5 Nov 2020 10:48:38 +0300 Subject: [PATCH 054/156] Update inter-library version constraints --- servant-client-core/servant-client-core.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-server/servant-server.cabal | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index b491ba2d..201821b3 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -64,7 +64,7 @@ library -- Servant dependencies build-depends: - servant >= 0.18 && <0.19 + servant >= 0.18.1 && <0.19 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 69352821..e72b9cb4 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -58,7 +58,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18 && <0.18.2 + , servant-client-core >= 0.18.1 && <0.18.2 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index b3c1e2fc..68fbaf4c 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -55,7 +55,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18 && <0.19.1 + , servant-client-core >= 0.18.1 && <0.18.2 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 5ea2d40f..19baec97 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -71,7 +71,7 @@ library -- Servant dependencies -- strict dependency as we re-export 'servant' things. build-depends: - servant >= 0.18 && < 0.19.1 + servant >= 0.18.1 && < 0.18.2 , http-api-data >= 0.4.1 && < 0.4.3 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. From 0ea692bb64273ae39ba3186cf4b33007e1a3d7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=A0=D0=B0=D0=B7?= =?UTF-8?q?=D0=BC=D0=B0=D1=85=D0=BD=D0=B8=D0=BD?= Date: Wed, 11 Nov 2020 10:22:55 +0300 Subject: [PATCH 055/156] Add support of Pretty modifier for all verbs aliases Minor import warning fix --- servant-docs/src/Servant/Docs/Internal.hs | 4 +--- .../src/Servant/Docs/Internal/Pretty.hs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index e9e95692..eff99913 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -32,9 +32,7 @@ import Data.ByteString.Lazy.Char8 (ByteString) import qualified Data.CaseInsensitive as CI import Data.Foldable - (toList) -import Data.Foldable - (fold) + (fold, toList) import Data.Hashable (Hashable) import Data.HashMap.Strict diff --git a/servant-docs/src/Servant/Docs/Internal/Pretty.hs b/servant-docs/src/Servant/Docs/Internal/Pretty.hs index 568ce26d..ac82c945 100644 --- a/servant-docs/src/Servant/Docs/Internal/Pretty.hs +++ b/servant-docs/src/Servant/Docs/Internal/Pretty.hs @@ -18,6 +18,7 @@ import Data.Proxy import Network.HTTP.Media ((//)) import Servant.API +import Servant.API.Verbs -- | PrettyJSON content type. data PrettyJSON @@ -46,6 +47,24 @@ type family Pretty (api :: k) :: k where Pretty (Put cs r) = Put (Pretty cs) r Pretty (Delete cs r) = Delete (Pretty cs) r Pretty (Patch cs r) = Patch (Pretty cs) r + Pretty (GetPartialContent cs r) = GetPartialContent (Pretty cs) r + Pretty (PutResetContent cs r) = PutResetContent (Pretty cs) r + Pretty (PatchResetContent cs r) = PatchResetContent (Pretty cs) r + Pretty (DeleteResetContent cs r) = DeleteResetContent (Pretty cs) r + Pretty (PostResetContent cs r) = PostResetContent (Pretty cs) r + Pretty (GetResetContent cs r) = GetResetContent (Pretty cs) r + Pretty (PutNonAuthoritative cs r) = PutNonAuthoritative (Pretty cs) r + Pretty (PatchNonAuthoritative cs r) = PatchNonAuthoritative (Pretty cs) r + Pretty (DeleteNonAuthoritative cs r) = DeleteNonAuthoritative (Pretty cs) r + Pretty (PostNonAuthoritative cs r) = PostNonAuthoritative (Pretty cs) r + Pretty (GetNonAuthoritative cs r) = GetNonAuthoritative (Pretty cs) r + Pretty (PutAccepted cs r) = PutAccepted (Pretty cs) r + Pretty (PatchAccepted cs r) = PatchAccepted (Pretty cs) r + Pretty (DeleteAccepted cs r) = DeleteAccepted (Pretty cs) r + Pretty (PostAccepted cs r) = PostAccepted (Pretty cs) r + Pretty (GetAccepted cs r) = GetAccepted (Pretty cs) r + Pretty (PutCreated cs r) = PutCreated (Pretty cs) r + Pretty (PostCreated cs r) = PostCreated (Pretty cs) r Pretty (ReqBody cs r) = ReqBody (Pretty cs) r Pretty (JSON ': xs) = PrettyJSON ': xs Pretty (x ': xs) = x ': Pretty xs From 339eec6a90327870fd14085f7adb782eead9c051 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Wed, 18 Nov 2020 17:33:03 +0100 Subject: [PATCH 056/156] 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 --- .travis.yml | 13 ++++++++++-- doc/cookbook/uverb/uverb.cabal | 2 +- servant/src/Servant/API/UVerb.hs | 36 ++++++++++++++++++++------------ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e609a77..79214952 100644 --- a/.travis.yml +++ b/.travis.yml @@ -148,6 +148,7 @@ install: if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/generic" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/pagination" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/testing" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/uverb" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/structuring-apis" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-custom-monad" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-free-client" >> cabal.project ; fi @@ -191,6 +192,8 @@ install: - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-uverb' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi @@ -210,7 +213,7 @@ install: echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project echo "allow-newer: servant-js:servant" >> 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-custom-errors|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" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|cookbook-uverb|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 @@ -233,6 +236,7 @@ install: - 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/uverb/configure.ac" ]; then (cd "doc/cookbook/uverb" && 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 @@ -271,6 +275,7 @@ script: - 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_uverb="$(find . -maxdepth 1 -type d -regex '.*/cookbook-uverb-[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.]*')" @@ -298,6 +303,7 @@ script: if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_generic}" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_pagination}" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_testing}" >> cabal.project ; fi + if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_uverb}" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_structuring_apis}" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_custom_monad}" >> cabal.project ; fi if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_free_client}" >> cabal.project ; fi @@ -341,6 +347,8 @@ script: - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" + - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-uverb' >> cabal.project ; fi + - "if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi @@ -360,7 +368,7 @@ script: echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project echo "allow-newer: servant-js:servant" >> 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-custom-errors|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" + - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|cookbook-uverb|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 - | @@ -386,6 +394,7 @@ script: cookbook-generic) echo ${PKGDIR_cookbook_generic} ;; cookbook-pagination) echo ${PKGDIR_cookbook_pagination} ;; cookbook-testing) echo ${PKGDIR_cookbook_testing} ;; + cookbook-uverb) echo ${PKGDIR_cookbook_uverb} ;; 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} ;; diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal index 0388365d..8b6b0b45 100644 --- a/doc/cookbook/uverb/uverb.cabal +++ b/doc/cookbook/uverb/uverb.cabal @@ -10,7 +10,7 @@ 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.4, GHC==8.10.1 executable cookbook-uverb main-is: UVerb.lhs diff --git a/servant/src/Servant/API/UVerb.hs b/servant/src/Servant/API/UVerb.hs index 4074adef..f7b1f740 100644 --- a/servant/src/Servant/API/UVerb.hs +++ b/servant/src/Servant/API/UVerb.hs @@ -4,14 +4,13 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | An alternative to 'Verb' for end-points that respond with a resource value of any of an -- open union of types, and specific status codes for each type in this union. (`UVerb` is @@ -34,12 +33,10 @@ module Servant.API.UVerb ) where -import Data.Aeson (FromJSON, ToJSON) import Data.Proxy (Proxy (Proxy)) -import qualified GHC.Generics as GHC import GHC.TypeLits (Nat) import Network.HTTP.Types (Status, StdMethod) -import Servant.API.ContentTypes (MimeRender (mimeRender), MimeUnrender (mimeUnrender), NoContent) +import Servant.API.ContentTypes (NoContent, MimeRender(mimeRender), MimeUnrender(mimeUnrender)) import Servant.API.Status (KnownStatus, statusVal) import Servant.API.UVerb.Union @@ -49,9 +46,6 @@ class KnownStatus (StatusOf a) => HasStatus (a :: *) where statusOf :: forall a proxy. HasStatus a => proxy a -> Status statusOf = const (statusVal (Proxy :: Proxy (StatusOf a))) -instance KnownStatus n => HasStatus (WithStatus n a) where - type StatusOf (WithStatus n a) = n - -- | If an API can respond with 'NoContent' we assume that this will happen -- with the status code 204 No Content. If this needs to be overridden, -- 'WithStatus' can be used. @@ -70,12 +64,10 @@ instance (HasStatus a, HasStatuses as) => HasStatuses (a ': as) where type Statuses (a ': as) = StatusOf a ': Statuses as statuses _ = statusOf (Proxy :: Proxy a) : statuses (Proxy :: Proxy as) +-- | A simple newtype wrapper that pairs a type with its status code. It +-- implements all the content types that Servant ships with by default. newtype WithStatus (k :: Nat) a = WithStatus a - deriving (Eq, Show, GHC.Generic) - -instance (GHC.Generic (WithStatus n a), ToJSON a) => ToJSON (WithStatus n a) - -instance (GHC.Generic (WithStatus n a), FromJSON a) => FromJSON (WithStatus n a) + deriving (Eq, Show) instance MimeRender ctype a => MimeRender ctype (WithStatus _status a) where mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a @@ -84,6 +76,24 @@ instance MimeUnrender ctype a => MimeUnrender ctype (WithStatus _status a) where mimeUnrender contentTypeProxy input = WithStatus <$> mimeUnrender contentTypeProxy input +-- | an instance of this typeclass assigns a HTTP status code to a return type +-- +-- Example: +-- +-- @ +-- data NotFoundError = NotFoundError String +-- +-- instance HasStatus NotFoundError where +-- type StatusOf NotFoundError = 404 +-- @ +-- +-- You can also use the convience newtype wrapper 'WithStatus' if you want to +-- avoid writing a 'HasStatus' instance manually. It also has the benefit of +-- showing the status code in the type; which might aid in readability. +instance KnownStatus n => HasStatus (WithStatus n a) where + type StatusOf (WithStatus n a) = n + + -- | A variant of 'Verb' that can have any of a number of response values and status codes. -- -- FUTUREWORK: it would be nice to make 'Verb' a special case of 'UVerb', and only write From da0c83d3184b7ca321d237053c5370d46cef2fe2 Mon Sep 17 00:00:00 2001 From: Andrey Prokopenko Date: Wed, 18 Nov 2020 21:57:20 +0300 Subject: [PATCH 057/156] Add URI fragment as a separate combinator (#1324) --- .../src/Servant/Client/Core/HasClient.hs | 48 +++++++++++-- .../test/Servant/ClientTestUtils.hs | 65 ++++++++++-------- servant-client/test/Servant/SuccessSpec.hs | 43 ++++++------ servant-docs/golden/comprehensive.md | 22 ++++++ servant-docs/src/Servant/Docs/Internal.hs | 59 ++++++++++++++-- servant-docs/test/Servant/DocsSpec.hs | 2 + .../src/Servant/Foreign/Internal.hs | 18 ++++- servant-foreign/test/Servant/ForeignSpec.hs | 7 +- servant-server/src/Servant/Server/Internal.hs | 45 +++++++++--- .../Server/Internal/RoutingApplicationSpec.hs | 4 +- servant-server/test/Servant/ServerSpec.hs | 62 +++++++++++++---- servant/servant.cabal | 1 + servant/src/Servant/API.hs | 21 +++--- servant/src/Servant/API/Fragment.hs | 25 +++++++ servant/src/Servant/API/Modifiers.hs | 2 - servant/src/Servant/API/TypeLevel.hs | 68 ++++++++++++++++--- servant/src/Servant/Links.hs | 36 ++++++++-- servant/src/Servant/Test/ComprehensiveAPI.hs | 1 + servant/test/Servant/LinksSpec.hs | 9 ++- 19 files changed, 418 insertions(+), 120 deletions(-) create mode 100644 servant/src/Servant/API/Fragment.hs diff --git a/servant-client-core/src/Servant/Client/Core/HasClient.hs b/servant-client-core/src/Servant/Client/Core/HasClient.hs index d515d120..f9e087f5 100644 --- a/servant-client-core/src/Servant/Client/Core/HasClient.hs +++ b/servant-client-core/src/Servant/Client/Core/HasClient.hs @@ -1,4 +1,5 @@ {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} @@ -13,6 +14,10 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} +#if MIN_VERSION_base(4,9,0) && __GLASGOW_HASKELL__ >= 802 +#define HAS_TYPE_ERROR +#endif + module Servant.Client.Core.HasClient ( clientIn, HasClient (..), @@ -63,17 +68,18 @@ import qualified Network.HTTP.Types as H import Servant.API ((:<|>) ((:<|>)), (:>), AuthProtect, BasicAuth, BasicAuthData, BuildHeadersTo (..), Capture', CaptureAll, Description, - EmptyAPI, FramingRender (..), FramingUnrender (..), + EmptyAPI, Fragment, FramingRender (..), FramingUnrender (..), FromSourceIO (..), Header', Headers (..), HttpVersion, IsSecure, MimeRender (mimeRender), - MimeUnrender (mimeUnrender), NoContent (NoContent), QueryFlag, - QueryParam', QueryParams, Raw, ReflectMethod (..), RemoteHost, - ReqBody', SBoolI, Stream, StreamBody', Summary, ToHttpApiData, - ToSourceIO (..), Vault, Verb, NoContentVerb, WithNamedContext, - contentType, getHeadersHList, getResponse, toQueryParam, - toUrlPiece) + MimeUnrender (mimeUnrender), NoContent (NoContent), + NoContentVerb, QueryFlag, QueryParam', QueryParams, Raw, + ReflectMethod (..), RemoteHost, ReqBody', SBoolI, Stream, + StreamBody', Summary, ToHttpApiData, ToSourceIO (..), Vault, + Verb, WithNamedContext, contentType, getHeadersHList, + getResponse, toQueryParam, toUrlPiece) import Servant.API.ContentTypes (contentTypes, AllMime (allMime), AllMimeUnrender (allMimeUnrender)) +import Servant.API.TypeLevel (FragmentUnique, AtLeastOneFragment) import Servant.API.Modifiers (FoldRequired, RequiredArgument, foldRequiredArgument) import Servant.API.UVerb @@ -745,6 +751,34 @@ instance ( HasClient m api hoistClientMonad pm _ f cl = \authreq -> hoistClientMonad pm (Proxy :: Proxy api) f (cl authreq) +-- | Ignore @'Fragment'@ in client functions. +-- See for more details. +-- +-- Example: +-- +-- > type MyApi = "books" :> Fragment Text :> Get '[JSON] [Book] +-- > +-- > myApi :: Proxy MyApi +-- > myApi = Proxy +-- > +-- > getBooksBy :: Maybe Text -> ClientM [Book] +-- > getBooksBy = client myApi +-- > -- then you can just use "getBooksBy" to query that endpoint. +-- > -- 'getBooksBy Nothing' for all books +-- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov +#ifdef HAS_TYPE_ERROR +instance (AtLeastOneFragment api, FragmentUnique (Fragment a :> api), HasClient m api +#else +instance ( HasClient m api +#endif + ) => HasClient m (Fragment a :> api) where + + type Client m (Fragment a :> api) = Client m api + + clientWithRoute pm _ = clientWithRoute pm (Proxy :: Proxy api) + + hoistClientMonad pm _ = hoistClientMonad pm (Proxy :: Proxy api) + -- * Basic Authentication instance HasClient m api => HasClient m (BasicAuth realm usr :> api) where diff --git a/servant-client/test/Servant/ClientTestUtils.hs b/servant-client/test/Servant/ClientTestUtils.hs index 0864896d..4b70a7a9 100644 --- a/servant-client/test/Servant/ClientTestUtils.hs +++ b/servant-client/test/Servant/ClientTestUtils.hs @@ -1,18 +1,18 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -freduction-depth=100 #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} @@ -27,20 +27,22 @@ import Control.Concurrent import Control.Monad.Error.Class (throwError) import Data.Aeson -import qualified Data.ByteString.Lazy as LazyByteString +import qualified Data.ByteString.Lazy as LazyByteString import Data.Char (chr, isPrint) import Data.Monoid () import Data.Proxy -import Data.Text (Text) -import qualified Data.Text as Text -import Data.Text.Encoding (encodeUtf8, decodeUtf8) +import Data.Text + (Text) +import qualified Data.Text as Text +import Data.Text.Encoding + (decodeUtf8, encodeUtf8) import GHC.Generics (Generic) -import qualified Network.HTTP.Client as C -import qualified Network.HTTP.Types as HTTP +import qualified Network.HTTP.Client as C +import qualified Network.HTTP.Types as HTTP import Network.Socket -import qualified Network.Wai as Wai +import qualified Network.Wai as Wai import Network.Wai.Handler.Warp import System.IO.Unsafe (unsafePerformIO) @@ -50,15 +52,14 @@ import Web.FormUrlEncoded import Servant.API ((:<|>) ((:<|>)), (:>), AuthProtect, BasicAuth, - BasicAuthData (..), Capture, CaptureAll, - DeleteNoContent, EmptyAPI, FormUrlEncoded, Get, Header, - Headers, JSON, MimeRender(mimeRender), - MimeUnrender(mimeUnrender), NoContent (NoContent), PlainText, - Post, QueryFlag, QueryParam, QueryParams, Raw, ReqBody, - StdMethod(GET), Union, UVerb, WithStatus(WithStatus), - addHeader) + BasicAuthData (..), Capture, CaptureAll, DeleteNoContent, + EmptyAPI, FormUrlEncoded, Fragment, Get, Header, Headers, + JSON, MimeRender (mimeRender), MimeUnrender (mimeUnrender), + NoContent (NoContent), PlainText, Post, QueryFlag, QueryParam, + QueryParams, Raw, ReqBody, StdMethod (GET), UVerb, Union, + WithStatus (WithStatus), addHeader) import Servant.Client -import qualified Servant.Client.Core.Auth as Auth +import qualified Servant.Client.Core.Auth as Auth import Servant.Server import Servant.Server.Experimental.Auth import Servant.Test.ComprehensiveAPI @@ -109,6 +110,7 @@ type Api = :<|> "param" :> QueryParam "name" String :> Get '[FormUrlEncoded,JSON] Person :<|> "params" :> QueryParams "names" String :> Get '[JSON] [Person] :<|> "flag" :> QueryFlag "flag" :> Get '[JSON] Bool + :<|> "fragment" :> Fragment String :> Get '[JSON] Person :<|> "rawSuccess" :> Raw :<|> "rawSuccessPassHeaders" :> Raw :<|> "rawFailure" :> Raw @@ -141,6 +143,7 @@ getBody :: Person -> ClientM Person getQueryParam :: Maybe String -> ClientM Person getQueryParams :: [String] -> ClientM [Person] getQueryFlag :: Bool -> ClientM Bool +getFragment :: ClientM Person getRawSuccess :: HTTP.Method -> ClientM Response getRawSuccessPassHeaders :: HTTP.Method -> ClientM Response getRawFailure :: HTTP.Method -> ClientM Response @@ -163,6 +166,7 @@ getRoot :<|> getQueryParam :<|> getQueryParams :<|> getQueryFlag + :<|> getFragment :<|> getRawSuccess :<|> getRawSuccessPassHeaders :<|> getRawFailure @@ -188,6 +192,7 @@ server = serve api ( Nothing -> throwError $ ServerError 400 "missing parameter" "" []) :<|> (\ names -> return (zipWith Person names [0..])) :<|> return + :<|> return alice :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.ok200 [] "rawSuccess") :<|> (Tagged $ \ request respond -> (respond $ Wai.responseLBS HTTP.ok200 (Wai.requestHeaders $ request) "rawSuccess")) :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.badRequest400 [] "rawFailure") diff --git a/servant-client/test/Servant/SuccessSpec.hs b/servant-client/test/Servant/SuccessSpec.hs index 9e12f034..bb9d47dc 100644 --- a/servant-client/test/Servant/SuccessSpec.hs +++ b/servant-client/test/Servant/SuccessSpec.hs @@ -1,17 +1,17 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -freduction-depth=100 #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} @@ -34,20 +34,21 @@ import Data.Maybe import Data.Monoid () import Data.Text (Text) -import qualified Network.HTTP.Client as C -import qualified Network.HTTP.Types as HTTP +import qualified Network.HTTP.Client as C +import qualified Network.HTTP.Types as HTTP import Test.Hspec import Test.Hspec.QuickCheck import Test.HUnit import Test.QuickCheck import Servant.API - (NoContent (NoContent), WithStatus(WithStatus), getHeaders) + (NoContent (NoContent), WithStatus (WithStatus), getHeaders) import Servant.Client -import qualified Servant.Client.Core.Request as Req -import Servant.Client.Internal.HttpClient (defaultMakeClientRequest) -import Servant.Test.ComprehensiveAPI +import qualified Servant.Client.Core.Request as Req +import Servant.Client.Internal.HttpClient + (defaultMakeClientRequest) import Servant.ClientTestUtils +import Servant.Test.ComprehensiveAPI -- This declaration simply checks that all instances are in place. _ = client comprehensiveAPIWithoutStreaming @@ -103,6 +104,8 @@ successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do forM_ [False, True] $ \ flag -> it (show flag) $ \(_, baseUrl) -> do left show <$> runClient (getQueryFlag flag) baseUrl `shouldReturn` Right flag + it "Servant.API.Fragment" $ \(_, baseUrl) -> do + left id <$> runClient getFragment baseUrl `shouldReturn` Right alice it "Servant.API.Raw on success" $ \(_, baseUrl) -> do res <- runClient (getRawSuccess HTTP.methodGet) baseUrl case res of diff --git a/servant-docs/golden/comprehensive.md b/servant-docs/golden/comprehensive.md index 5bb7c4e9..7d224502 100644 --- a/servant-docs/golden/comprehensive.md +++ b/servant-docs/golden/comprehensive.md @@ -182,6 +182,28 @@ ``` +## GET /fragment + +### Fragment: + +- *foo*: Fragment Int + +### Response: + +- Status code 200 +- Headers: [] + +- Supported content types are: + + - `application/json;charset=utf-8` + - `application/json` + +- Example (`application/json;charset=utf-8`, `application/json`): + +```javascript + +``` + ## GET /get-int ### Response: diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index e9e95692..b36ad88b 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -25,8 +25,8 @@ import Control.Applicative import Control.Arrow (second) import Control.Lens - (makeLenses, mapped, over, traversed, view, (%~), (&), (.~), - (<>~), (^.), (|>)) + (makeLenses, mapped, over, set, traversed, view, (%~), (&), + (.~), (<>~), (^.), (|>)) import qualified Data.ByteString.Char8 as BSC import Data.ByteString.Lazy.Char8 (ByteString) @@ -64,7 +64,7 @@ import Servant.API import Servant.API.ContentTypes import Servant.API.TypeLevel -import qualified Data.Universe.Helpers as U +import qualified Data.Universe.Helpers as U import qualified Data.HashMap.Strict as HM import qualified Data.Text as T @@ -161,6 +161,20 @@ data DocQueryParam = DocQueryParam , _paramKind :: ParamKind } deriving (Eq, Ord, Show) +-- | A type to represent fragment. Holds the name of the fragment and its description. +-- +-- Write a 'ToFragment' instance for your fragment types. +data DocFragment = DocFragment + { _fragSymbol :: String -- type supplied + , _fragDesc :: String -- user supplied + } deriving (Eq, Ord, Show) + +-- | There should be at most one 'Fragment' per API endpoint. +-- So here we are keeping the first occurrence. +combineFragment :: Maybe DocFragment -> Maybe DocFragment -> Maybe DocFragment +Nothing `combineFragment` mdocFragment = mdocFragment +Just docFragment `combineFragment` _ = Just docFragment + -- | An introductory paragraph for your documentation. You can pass these to -- 'docsWithIntros'. data DocIntro = DocIntro @@ -283,6 +297,7 @@ data Action = Action , _captures :: [DocCapture] -- type collected + user supplied info , _headers :: [Text] -- type collected , _params :: [DocQueryParam] -- type collected + user supplied info + , _fragment :: Maybe DocFragment -- type collected + user supplied info , _notes :: [DocNote] -- user supplied , _mxParams :: [(String, [DocQueryParam])] -- type collected + user supplied info , _rqtypes :: [M.MediaType] -- type collected @@ -296,8 +311,9 @@ data Action = Action -- As such, we invent a non-commutative, left associative operation -- 'combineAction' to mush two together taking the response from the very left. combineAction :: Action -> Action -> Action -Action a c h p n m ts body resp `combineAction` Action a' c' h' p' n' m' ts' body' resp' = - Action (a <> a') (c <> c') (h <> h') (p <> p') (n <> n') (m <> m') (ts <> ts') (body <> body') (resp `combineResponse` resp') +Action a c h p f n m ts body resp + `combineAction` Action a' c' h' p' f' n' m' ts' body' resp' = + Action (a <> a') (c <> c') (h <> h') (p <> p') (f `combineFragment` f') (n <> n') (m <> m') (ts <> ts') (body <> body') (resp `combineResponse` resp') -- | Default 'Action'. Has no 'captures', no query 'params', expects -- no request body ('rqbody') and the typical response is 'defResponse'. @@ -305,10 +321,10 @@ Action a c h p n m ts body resp `combineAction` Action a' c' h' p' n' m' ts' bod -- Tweakable with lenses. -- -- >>> defAction --- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 200, _respTypes = [], _respBody = [], _respHeaders = []}} +-- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _fragment = Nothing, _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 200, _respTypes = [], _respBody = [], _respHeaders = []}} -- -- >>> defAction & response.respStatus .~ 201 --- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 201, _respTypes = [], _respBody = [], _respHeaders = []}} +-- Action {_authInfo = [], _captures = [], _headers = [], _params = [], _fragment = Nothing, _notes = [], _mxParams = [], _rqtypes = [], _rqbody = [], _response = Response {_respStatus = 201, _respTypes = [], _respBody = [], _respHeaders = []}} -- defAction :: Action defAction = @@ -316,6 +332,7 @@ defAction = [] [] [] + Nothing [] [] [] @@ -368,6 +385,7 @@ makeLenses ''API makeLenses ''Endpoint makeLenses ''DocCapture makeLenses ''DocQueryParam +makeLenses ''DocFragment makeLenses ''DocIntro makeLenses ''DocNote makeLenses ''Response @@ -587,6 +605,15 @@ class ToCapture c where class ToAuthInfo a where toAuthInfo :: Proxy a -> DocAuthentication +-- | The class that helps us get documentation for URL fragments. +-- +-- Example of an instance: +-- +-- > instance ToFragment (Fragment a) where +-- > toFragment _ = DocFragment "fragment" "fragment description" +class ToFragment t where + toFragment :: Proxy t -> DocFragment + -- | Generate documentation in Markdown format for -- the given 'API'. -- @@ -629,6 +656,7 @@ markdownWith RenderingOptions{..} api = unlines $ capturesStr (action ^. captures) ++ headersStr (action ^. headers) ++ paramsStr meth (action ^. params) ++ + fragmentStr (action ^. fragment) ++ rqbodyStr (action ^. rqtypes) (action ^. rqbody) ++ responseStr (action ^. response) ++ [] @@ -730,6 +758,14 @@ markdownWith RenderingOptions{..} api = unlines $ where values = param ^. paramValues + fragmentStr :: Maybe DocFragment -> [String] + fragmentStr Nothing = [] + fragmentStr (Just frag) = + [ "### Fragment:", "" + , "- *" ++ (frag ^. fragSymbol) ++ "*: " ++ (frag ^. fragDesc) + , "" + ] + rqbodyStr :: [M.MediaType] -> [(Text, M.MediaType, ByteString)]-> [String] rqbodyStr [] [] = [] rqbodyStr types s = @@ -959,6 +995,15 @@ instance (KnownSymbol sym, ToParam (QueryFlag sym), HasDocs api) paramP = Proxy :: Proxy (QueryFlag sym) action' = over params (|> toParam paramP) action +instance (ToFragment (Fragment a), HasDocs api) + => HasDocs (Fragment a :> api) where + + docsFor Proxy (endpoint, action) = + docsFor subApiP (endpoint, action') + + where subApiP = Proxy :: Proxy api + fragmentP = Proxy :: Proxy (Fragment a) + action' = set fragment (Just (toFragment fragmentP)) action instance HasDocs Raw where docsFor _proxy (endpoint, action) _ = diff --git a/servant-docs/test/Servant/DocsSpec.hs b/servant-docs/test/Servant/DocsSpec.hs index 8a297b4d..5da5ff4d 100644 --- a/servant-docs/test/Servant/DocsSpec.hs +++ b/servant-docs/test/Servant/DocsSpec.hs @@ -58,6 +58,8 @@ instance ToCapture (Capture "foo" Int) where toCapture _ = DocCapture "foo" "Capture foo Int" instance ToCapture (CaptureAll "foo" Int) where toCapture _ = DocCapture "foo" "Capture all foo Int" +instance ToFragment (Fragment Int) where + toFragment _ = DocFragment "foo" "Fragment Int" -- * specs diff --git a/servant-foreign/src/Servant/Foreign/Internal.hs b/servant-foreign/src/Servant/Foreign/Internal.hs index 0f3b1248..22f37ad9 100644 --- a/servant-foreign/src/Servant/Foreign/Internal.hs +++ b/servant-foreign/src/Servant/Foreign/Internal.hs @@ -84,6 +84,11 @@ captureArg _ = error "captureArg called on non capture" type Path f = [Segment f] +newtype Frag f = Frag { unFragment :: Arg f } + deriving (Data, Eq, Show, Typeable) + +makePrisms ''Frag + data ArgType = Normal | Flag @@ -115,11 +120,12 @@ makePrisms ''HeaderArg data Url f = Url { _path :: Path f , _queryStr :: [QueryArg f] + , _frag :: Maybe f } deriving (Data, Eq, Show, Typeable) defUrl :: Url f -defUrl = Url [] [] +defUrl = Url [] [] Nothing makeLenses ''Url @@ -324,6 +330,16 @@ instance { _argName = PathSegment str , _argType = typeFor lang ftype (Proxy :: Proxy Bool) } +instance + (HasForeignType lang ftype (Maybe a), HasForeign lang ftype api) + => HasForeign lang ftype (Fragment a :> api) where + type Foreign ftype (Fragment a :> api) = Foreign ftype api + foreignFor lang Proxy Proxy req = + foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy api) $ + req & reqUrl . frag .~ Just argT + where + argT = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy (Maybe a)) + instance HasForeign lang ftype Raw where type Foreign ftype Raw = HTTP.Method -> Req ftype diff --git a/servant-foreign/test/Servant/ForeignSpec.hs b/servant-foreign/test/Servant/ForeignSpec.hs index 76fb5351..3baaf7b6 100644 --- a/servant-foreign/test/Servant/ForeignSpec.hs +++ b/servant-foreign/test/Servant/ForeignSpec.hs @@ -14,8 +14,8 @@ module Servant.ForeignSpec where import Data.Monoid ((<>)) import Data.Proxy -import Servant.Test.ComprehensiveAPI import Servant.Foreign +import Servant.Test.ComprehensiveAPI import Servant.Types.SourceT (SourceT) @@ -91,6 +91,7 @@ listFromAPISpec = describe "listFromAPI" $ do { _reqUrl = Url [ Segment $ Static "test" ] [ QueryArg (Arg "flag" "boolX") Flag ] + Nothing , _reqMethod = "GET" , _reqHeaders = [HeaderArg $ Arg "header" "maybe listX of stringX"] , _reqBody = Nothing @@ -103,6 +104,7 @@ listFromAPISpec = describe "listFromAPI" $ do { _reqUrl = Url [ Segment $ Static "test" ] [ QueryArg (Arg "param" "maybe intX") Normal ] + Nothing , _reqMethod = "POST" , _reqHeaders = [] , _reqBody = Just "listX of stringX" @@ -116,6 +118,7 @@ listFromAPISpec = describe "listFromAPI" $ do [ Segment $ Static "test" ] -- Should this be |intX| or |listX of intX| ? [ QueryArg (Arg "params" "listX of intX") List ] + Nothing , _reqMethod = "PUT" , _reqHeaders = [] , _reqBody = Just "stringX" @@ -129,6 +132,7 @@ listFromAPISpec = describe "listFromAPI" $ do [ Segment $ Static "test" , Segment $ Cap (Arg "id" "intX") ] [] + Nothing , _reqMethod = "DELETE" , _reqHeaders = [] , _reqBody = Nothing @@ -142,6 +146,7 @@ listFromAPISpec = describe "listFromAPI" $ do [ Segment $ Static "test" , Segment $ Cap (Arg "ids" "listX of intX") ] [] + Nothing , _reqMethod = "GET" , _reqHeaders = [] , _reqBody = Nothing diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index ba42c8e9..8ebfa9a1 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -71,17 +71,17 @@ import Prelude () import Prelude.Compat import Servant.API ((:<|>) (..), (:>), Accept (..), BasicAuth, Capture', - CaptureAll, Description, EmptyAPI, FramingRender (..), - FramingUnrender (..), FromSourceIO (..), Header', If, - IsSecure (..), QueryFlag, QueryParam', QueryParams, Raw, - ReflectMethod (reflectMethod), RemoteHost, ReqBody', - SBool (..), SBoolI (..), SourceIO, Stream, StreamBody', - Summary, ToSourceIO (..), Vault, Verb, NoContentVerb, + CaptureAll, Description, EmptyAPI, Fragment, + FramingRender (..), FramingUnrender (..), FromSourceIO (..), + Header', If, IsSecure (..), NoContentVerb, QueryFlag, + QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod), + RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO, + Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb, WithNamedContext) import Servant.API.ContentTypes (AcceptHeader (..), AllCTRender (..), AllCTUnrender (..), - AllMime, MimeRender (..), MimeUnrender (..), canHandleAcceptH, - NoContent) + AllMime, MimeRender (..), MimeUnrender (..), NoContent, + canHandleAcceptH) import Servant.API.Modifiers (FoldLenient, FoldRequired, RequestArgument, unfoldRequestArgument) @@ -89,8 +89,8 @@ import Servant.API.ResponseHeaders (GetHeaders, Headers, getHeaders, getResponse) import qualified Servant.Types.SourceT as S import Web.HttpApiData - (FromHttpApiData, parseHeader, parseQueryParam, - parseUrlPieces, parseUrlPiece) + (FromHttpApiData, parseHeader, parseQueryParam, parseUrlPiece, + parseUrlPieces) import Servant.Server.Internal.BasicAuth import Servant.Server.Internal.Context @@ -106,6 +106,8 @@ import Servant.Server.Internal.ServerError #ifdef HAS_TYPE_ERROR import GHC.TypeLits (ErrorMessage (..), TypeError) +import Servant.API.TypeLevel + (AtLeastOneFragment, FragmentUnique) #endif class HasServer api context where @@ -880,5 +882,28 @@ type HasServerArrowTypeError a b = ':$$: 'ShowType b #endif +-- | Ignore @'Fragment'@ in server handlers. +-- See for more details. +-- +-- Example: +-- +-- > type MyApi = "books" :> Fragment Text :> Get '[JSON] [Book] +-- > +-- > server :: Server MyApi +-- > server = getBooksBy +-- > where getBooksBy :: Handler [Book] +-- > getBooksBy = ...return all books... +#ifdef HAS_TYPE_ERROR +instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context) +#else +instance (HasServer api context) +#endif + => HasServer (Fragment a1 :> api) context where + type ServerT (Fragment a1 :> api) m = ServerT api m + + route _ = route (Proxy :: Proxy api) + + hoistServerWithContext _ = hoistServerWithContext (Proxy :: Proxy api) + -- $setup -- >>> import Servant diff --git a/servant-server/test/Servant/Server/Internal/RoutingApplicationSpec.hs b/servant-server/test/Servant/Server/Internal/RoutingApplicationSpec.hs index 80210495..04443c9d 100644 --- a/servant-server/test/Servant/Server/Internal/RoutingApplicationSpec.hs +++ b/servant-server/test/Servant/Server/Internal/RoutingApplicationSpec.hs @@ -11,7 +11,7 @@ module Servant.Server.Internal.RoutingApplicationSpec (spec) where import Prelude () import Prelude.Compat -import Control.Exception hiding +import Control.Exception hiding (Handler) import Control.Monad.IO.Class import Control.Monad.Trans.Resource @@ -28,7 +28,7 @@ import Test.Hspec import Test.Hspec.Wai (request, shouldRespondWith, with) -import qualified Data.Text as T +import qualified Data.Text as T import System.IO.Unsafe (unsafePerformIO) diff --git a/servant-server/test/Servant/ServerSpec.hs b/servant-server/test/Servant/ServerSpec.hs index 87c84421..e3dec48e 100644 --- a/servant-server/test/Servant/ServerSpec.hs +++ b/servant-server/test/Servant/ServerSpec.hs @@ -1,12 +1,12 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -freduction-depth=100 #-} module Servant.ServerSpec where @@ -48,12 +48,12 @@ import Network.Wai.Test import Servant.API ((:<|>) (..), (:>), AuthProtect, BasicAuth, BasicAuthData (BasicAuthData), Capture, Capture', CaptureAll, - Delete, EmptyAPI, Get, HasStatus(StatusOf), Header, Headers, - HttpVersion, IsSecure (..), JSON, Lenient, NoContent (..), - NoContentVerb, NoFraming, OctetStream, Patch, PlainText, Post, - Put, QueryFlag, QueryParam, QueryParams, Raw, RemoteHost, - ReqBody, SourceIO, StdMethod (..), Stream, Strict, Union, - UVerb, Verb, addHeader) + Delete, EmptyAPI, Fragment, Get, HasStatus (StatusOf), Header, + Headers, HttpVersion, IsSecure (..), JSON, Lenient, + NoContent (..), NoContentVerb, NoFraming, OctetStream, Patch, + PlainText, Post, Put, QueryFlag, QueryParam, QueryParams, Raw, + RemoteHost, ReqBody, SourceIO, StdMethod (..), Stream, Strict, + UVerb, Union, Verb, addHeader) import Servant.Server (Context ((:.), EmptyContext), Handler, Server, Tagged (..), emptyServer, err401, err403, err404, respond, serve, @@ -92,6 +92,7 @@ spec = do captureSpec captureAllSpec queryParamSpec + fragmentSpec reqBodySpec headerSpec rawSpec @@ -461,6 +462,37 @@ queryParamSpec = do { name = "Alice" } +-- }}} +------------------------------------------------------------------------------ +-- * fragmentSpec {{{ +------------------------------------------------------------------------------ + +type FragmentApi = "name" :> Fragment String :> Get '[JSON] Person + :<|> "age" :> Fragment Integer :> Get '[JSON] Person + +fragmentApi :: Proxy FragmentApi +fragmentApi = Proxy + +fragServer :: Server FragmentApi +fragServer = fragmentServer :<|> fragAge + where + fragmentServer = return alice + fragAge = return alice + +fragmentSpec :: Spec +fragmentSpec = do + let mkRequest params pinfo = Network.Wai.Test.request defaultRequest + { rawQueryString = params + , queryString = parseQuery params + , pathInfo = pinfo + } + + describe "Servant.API.Fragment" $ do + it "ignores fragment even if it is present in query" $ do + flip runSession (serve fragmentApi fragServer) $ do + response1 <- mkRequest "#Alice" ["name"] + liftIO $ decode' (simpleBody response1) `shouldBe` Just alice + -- }}} ------------------------------------------------------------------------------ -- * reqBodySpec {{{ diff --git a/servant/servant.cabal b/servant/servant.cabal index 79fcd5ff..469f6cec 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -40,6 +40,7 @@ library Servant.API.Description Servant.API.Empty Servant.API.Experimental.Auth + Servant.API.Fragment Servant.API.Generic Servant.API.Header Servant.API.HttpVersion diff --git a/servant/src/Servant/API.hs b/servant/src/Servant/API.hs index 66b86d78..deb974ae 100644 --- a/servant/src/Servant/API.hs +++ b/servant/src/Servant/API.hs @@ -19,6 +19,8 @@ module Servant.API ( -- | Retrieving the HTTP version of the request module Servant.API.QueryParam, -- | Retrieving parameters from the query string of the 'URI': @'QueryParam'@ + module Servant.API.Fragment, + -- | Documenting the fragment of the 'URI': @'Fragment'@ module Servant.API.ReqBody, -- | Accessing the request body as a JSON-encoded type: @'ReqBody'@ module Servant.API.RemoteHost, @@ -93,6 +95,8 @@ import Servant.API.Empty (EmptyAPI (..)) import Servant.API.Experimental.Auth (AuthProtect) +import Servant.API.Fragment + (Fragment) import Servant.API.Header (Header, Header') import Servant.API.HttpVersion @@ -121,21 +125,20 @@ import Servant.API.Stream ToSourceIO (..)) import Servant.API.Sub ((:>)) +import Servant.API.UVerb + (HasStatus, IsMember, StatusOf, Statuses, UVerb, Union, + Unique, WithStatus (..), inject, statusOf) import Servant.API.Vault (Vault) import Servant.API.Verbs (Delete, DeleteAccepted, DeleteNoContent, DeleteNonAuthoritative, Get, GetAccepted, GetNoContent, GetNonAuthoritative, GetPartialContent, GetResetContent, - Patch, PatchAccepted, PatchNoContent, PatchNonAuthoritative, - Post, PostAccepted, PostCreated, PostNoContent, - PostNonAuthoritative, PostResetContent, Put, PutAccepted, - PutCreated, PutNoContent, PutNonAuthoritative, - ReflectMethod (reflectMethod), StdMethod (..), - Verb, NoContentVerb) -import Servant.API.UVerb - (UVerb, Union, HasStatus, StatusOf, statusOf, Statuses, - WithStatus (..), IsMember, Unique, inject) + NoContentVerb, Patch, PatchAccepted, PatchNoContent, + PatchNonAuthoritative, Post, PostAccepted, PostCreated, + PostNoContent, PostNonAuthoritative, PostResetContent, Put, + PutAccepted, PutCreated, PutNoContent, PutNonAuthoritative, + ReflectMethod (reflectMethod), StdMethod (..), Verb) import Servant.API.WithNamedContext (WithNamedContext) import Servant.Links diff --git a/servant/src/Servant/API/Fragment.hs b/servant/src/Servant/API/Fragment.hs new file mode 100644 index 00000000..dd9befaa --- /dev/null +++ b/servant/src/Servant/API/Fragment.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE TypeOperators #-} +{-# OPTIONS_HADDOCK not-home #-} +module Servant.API.Fragment (Fragment) where + +import Data.Typeable + (Typeable) + +-- | Document the URI fragment in API. Useful in combination with 'Link'. +-- +-- Example: +-- +-- >>> -- /post#TRACKING +-- >>> type MyApi = "post" :> Fragment Text :> Get '[JSON] Tracking +data Fragment (a :: *) + deriving Typeable + +-- $setup +-- >>> import Servant.API +-- >>> import Data.Aeson +-- >>> import Data.Text +-- >>> data Tracking +-- >>> instance ToJSON Tracking where { toJSON = undefined } diff --git a/servant/src/Servant/API/Modifiers.hs b/servant/src/Servant/API/Modifiers.hs index 7979ac15..3714fd3a 100644 --- a/servant/src/Servant/API/Modifiers.hs +++ b/servant/src/Servant/API/Modifiers.hs @@ -131,8 +131,6 @@ type RequestArgument mods a = (If (FoldLenient mods) (Either Text a) a) (Maybe (If (FoldLenient mods) (Either Text a) a)) - - -- | Unfold a value into a 'RequestArgument'. unfoldRequestArgument :: forall mods m a. (Monad m, SBoolI (FoldRequired mods), SBoolI (FoldLenient mods)) diff --git a/servant/src/Servant/API/TypeLevel.hs b/servant/src/Servant/API/TypeLevel.hs index 79ff287a..4a5e3c3b 100644 --- a/servant/src/Servant/API/TypeLevel.hs +++ b/servant/src/Servant/API/TypeLevel.hs @@ -1,12 +1,16 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE KindSignatures #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE PolyKinds #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE UndecidableSuperClasses #-} {-| This module collects utilities for manipulating @servant@ API types. The @@ -41,6 +45,9 @@ module Servant.API.TypeLevel ( -- ** Logic Or, And, + -- ** Fragment + FragmentUnique, + AtLeastOneFragment ) where @@ -50,6 +57,7 @@ import Servant.API.Alternative (type (:<|>)) import Servant.API.Capture (Capture, CaptureAll) +import Servant.API.Fragment import Servant.API.Header (Header) import Servant.API.QueryParam @@ -60,6 +68,8 @@ import Servant.API.Sub (type (:>)) import Servant.API.Verbs (Verb) +import Servant.API.UVerb + (UVerb) import GHC.TypeLits (ErrorMessage (..), TypeError) @@ -128,6 +138,7 @@ type family IsElem endpoint api :: Constraint where IsElem sa (QueryParam x y :> sb) = IsElem sa sb IsElem sa (QueryParams x y :> sb) = IsElem sa sb IsElem sa (QueryFlag x :> sb) = IsElem sa sb + IsElem sa (Fragment x :> sb) = IsElem sa sb IsElem (Verb m s ct typ) (Verb m s ct' typ) = IsSubList ct ct' IsElem e e = () @@ -241,6 +252,43 @@ We might try to factor these our more cleanly, but the type synonyms and type families are not evaluated (see https://ghc.haskell.org/trac/ghc/ticket/12048). -} +-- ** Fragment + +class FragmentUnique api => AtLeastOneFragment api + +-- | If fragment appeared in API endpoint twice, compile-time error would be raised. +-- +-- >>> -- type FailAPI = Fragment Bool :> Fragment Int :> Get '[JSON] NoContent +-- >>> instance AtLeastOneFragment FailAPI +-- ... +-- ...Only one Fragment allowed per endpoint in api... +-- ... +-- ...In the instance declaration for... +instance AtLeastOneFragment (Verb m s ct typ) + +instance AtLeastOneFragment (UVerb m cts as) + +instance AtLeastOneFragment (Fragment a) + +type family FragmentUnique api :: Constraint where + FragmentUnique (sa :<|> sb) = And (FragmentUnique sa) (FragmentUnique sb) + FragmentUnique (Fragment a :> sa) = FragmentNotIn sa (Fragment a :> sa) + FragmentUnique (x :> sa) = FragmentUnique sa + FragmentUnique (Fragment a) = () + FragmentUnique x = () + +type family FragmentNotIn api orig :: Constraint where + FragmentNotIn (sa :<|> sb) orig = + And (FragmentNotIn sa orig) (FragmentNotIn sb orig) + FragmentNotIn (Fragment c :> sa) orig = TypeError (NotUniqueFragmentInApi orig) + FragmentNotIn (x :> sa) orig = FragmentNotIn sa orig + FragmentNotIn (Fragment c) orig = TypeError (NotUniqueFragmentInApi orig) + FragmentNotIn x orig = () + +type NotUniqueFragmentInApi api = + 'Text "Only one Fragment allowed per endpoint in api ‘" + ':<>: 'ShowType api + ':<>: 'Text "’." -- $setup -- @@ -248,6 +296,7 @@ families are not evaluated (see https://ghc.haskell.org/trac/ghc/ticket/12048). -- -- >>> :set -XPolyKinds -- >>> :set -XGADTs +-- >>> :set -XTypeSynonymInstances -XFlexibleInstances -- >>> import Data.Proxy -- >>> import Data.Type.Equality -- >>> import Servant.API @@ -255,4 +304,5 @@ families are not evaluated (see https://ghc.haskell.org/trac/ghc/ticket/12048). -- >>> instance Show (OK ctx) where show _ = "OK" -- >>> let ok :: ctx => Proxy ctx -> OK ctx; ok _ = OK -- >>> type SampleAPI = "hello" :> Get '[JSON] Int :<|> "bye" :> Capture "name" String :> Post '[JSON, PlainText] Bool +-- >>> type FailAPI = Fragment Bool :> Fragment Int :> Get '[JSON] NoContent -- >>> let sampleAPI = Proxy :: Proxy SampleAPI diff --git a/servant/src/Servant/Links.hs b/servant/src/Servant/Links.hs index b42738e2..39a228d7 100644 --- a/servant/src/Servant/Links.hs +++ b/servant/src/Servant/Links.hs @@ -120,6 +120,7 @@ module Servant.Links ( , Param (..) , linkSegments , linkQueryParams + , linkFragment ) where import Data.List @@ -152,6 +153,8 @@ import Servant.API.Empty (EmptyAPI (..)) import Servant.API.Experimental.Auth (AuthProtect) +import Servant.API.Fragment + (Fragment) import Servant.API.Generic import Servant.API.Header (Header') @@ -188,10 +191,13 @@ import Web.HttpApiData data Link = Link { _segments :: [Escaped] , _queryParams :: [Param] + , _fragment :: Fragment' } deriving Show newtype Escaped = Escaped String +type Fragment' = Maybe String + escaped :: String -> Escaped escaped = Escaped . escapeURIString isUnreserved @@ -208,11 +214,14 @@ linkSegments = map getEscaped . _segments linkQueryParams :: Link -> [Param] linkQueryParams = _queryParams +linkFragment :: Link -> Fragment' +linkFragment = _fragment + instance ToHttpApiData Link where toHeader = TE.encodeUtf8 . toUrlPiece toUrlPiece l = let uri = linkURI l - in Text.pack $ uriPath uri ++ uriQuery uri + in Text.pack $ uriPath uri ++ uriQuery uri ++ uriFragment uri -- | Query parameter. data Param @@ -228,6 +237,9 @@ addQueryParam :: Param -> Link -> Link addQueryParam qp l = l { _queryParams = _queryParams l <> [qp] } +addFragment :: Fragment' -> Link -> Link +addFragment fr l = l { _fragment = fr } + -- | Transform 'Link' into 'URI'. -- -- >>> type API = "something" :> Get '[JSON] Int @@ -245,7 +257,7 @@ addQueryParam qp l = -- >>> type SomeRoute = "abc" :> Capture "email" String :> Put '[JSON] () -- >>> let someRoute = Proxy :: Proxy SomeRoute -- >>> safeLink someRoute someRoute "test@example.com" --- Link {_segments = ["abc","test%40example.com"], _queryParams = []} +-- Link {_segments = ["abc","test%40example.com"], _queryParams = [], _fragment = Nothing} -- -- >>> linkURI $ safeLink someRoute someRoute "test@example.com" -- abc/test%40example.com @@ -269,11 +281,12 @@ data LinkArrayElementStyle -- sum?x=1&x=2&x=3 -- linkURI' :: LinkArrayElementStyle -> Link -> URI -linkURI' addBrackets (Link segments q_params) = +linkURI' addBrackets (Link segments q_params mfragment) = URI mempty -- No scheme (relative) Nothing -- Or authority (relative) (intercalate "/" $ map getEscaped segments) - (makeQueries q_params) mempty + (makeQueries q_params) + (makeFragment mfragment) where makeQueries :: [Param] -> String makeQueries [] = "" @@ -285,6 +298,10 @@ linkURI' addBrackets (Link segments q_params) = makeQuery (SingleParam k v) = escape k <> "=" <> escape (Text.unpack v) makeQuery (FlagParam k) = escape k + makeFragment :: Fragment' -> String + makeFragment Nothing = "" + makeFragment (Just fr) = "#" <> escape fr + style = case addBrackets of LinkArrayElementBracket -> "[]=" LinkArrayElementPlain -> "=" @@ -310,7 +327,7 @@ safeLink' -> Proxy api -- ^ The whole API that this endpoint is a part of -> Proxy endpoint -- ^ The API endpoint you would like to point to -> MkLink endpoint a -safeLink' toA _ endpoint = toLink toA endpoint (Link mempty mempty) +safeLink' toA _ endpoint = toLink toA endpoint (Link mempty mempty mempty) -- | Create all links in an API. -- @@ -341,7 +358,7 @@ allLinks' => (Link -> a) -> Proxy api -> MkLink api a -allLinks' toA api = toLink toA api (Link mempty mempty) +allLinks' toA api = toLink toA api (Link mempty mempty mempty) ------------------------------------------------------------------------------- -- Generics @@ -563,6 +580,13 @@ instance HasLink sub => HasLink (AuthProtect tag :> sub) where type MkLink (AuthProtect tag :> sub) a = MkLink sub a toLink = simpleToLink (Proxy :: Proxy sub) +instance (HasLink sub, ToHttpApiData v) + => HasLink (Fragment v :> sub) where + type MkLink (Fragment v :> sub) a = v -> MkLink sub a + toLink toA _ l mv = + toLink toA (Proxy :: Proxy sub) $ + addFragment ((Just . Text.unpack . toQueryParam) mv) l + -- | Helper for implementing 'toLink' for combinators not affecting link -- structure. simpleToLink diff --git a/servant/src/Servant/Test/ComprehensiveAPI.hs b/servant/src/Servant/Test/ComprehensiveAPI.hs index 4445986a..2c1b02a3 100644 --- a/servant/src/Servant/Test/ComprehensiveAPI.hs +++ b/servant/src/Servant/Test/ComprehensiveAPI.hs @@ -71,6 +71,7 @@ type ComprehensiveAPIWithoutStreamingOrRaw' endpoint = :<|> "summary" :> Summary "foo" :> GET :<|> "description" :> Description "foo" :> GET :<|> "alternative" :> ("left" :> GET :<|> "right" :> GET) + :<|> "fragment" :> Fragment Int :> GET :<|> endpoint type ComprehensiveAPIWithoutStreamingOrRaw = ComprehensiveAPIWithoutStreamingOrRaw' EmptyEndpoint diff --git a/servant/test/Servant/LinksSpec.hs b/servant/test/Servant/LinksSpec.hs index 845f5ee7..9d45e4a9 100644 --- a/servant/test/Servant/LinksSpec.hs +++ b/servant/test/Servant/LinksSpec.hs @@ -13,9 +13,9 @@ import Test.Hspec (Expectation, Spec, describe, it, shouldBe) import Servant.API +import Servant.Links import Servant.Test.ComprehensiveAPI (comprehensiveAPIWithoutRaw) -import Servant.Links type TestApi = -- Capture and query params @@ -26,6 +26,9 @@ type TestApi = -- Flags :<|> "balls" :> QueryFlag "bouncy" :> QueryFlag "fast" :> Delete '[JSON] NoContent + -- Fragment + :<|> "say" :> Fragment String :> Get '[JSON] NoContent + -- All of the verbs :<|> "get" :> Get '[JSON] NoContent :<|> "put" :> Put '[JSON] NoContent @@ -76,6 +79,10 @@ spec = describe "Servant.Links" $ do apiLink l1 True True `shouldBeLink` "balls?bouncy&fast" apiLink l1 False True `shouldBeLink` "balls?fast" + it "generates correct link for fragment" $ do + let l1 = Proxy :: Proxy ("say" :> Fragment String :> Get '[JSON] NoContent) + apiLink l1 "something" `shouldBeLink` "say#something" + it "generates correct links for all of the verbs" $ do apiLink (Proxy :: Proxy ("get" :> Get '[JSON] NoContent)) `shouldBeLink` "get" apiLink (Proxy :: Proxy ("put" :> Put '[JSON] NoContent)) `shouldBeLink` "put" From 1d0b34df508d2e006ad98da6c29467e748b8c4fe Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sun, 22 Nov 2020 18:07:54 +0800 Subject: [PATCH 058/156] Allow QuickCheck 2.14 (#1359) Builds fine and all tests pass. --- servant-server/servant-server.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 19baec97..4a4e40e4 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -159,7 +159,7 @@ test-suite spec , directory >= 1.3.0.0 && < 1.4 , hspec >= 2.6.0 && < 2.8 , hspec-wai >= 0.10.1 && < 0.12 - , QuickCheck >= 2.12.6.1 && < 2.14 + , QuickCheck >= 2.12.6.1 && < 2.15 , should-not-typecheck >= 2.1.0 && < 2.2 , temporary >= 1.3 && < 1.4 , wai-extra >= 3.0.24.3 && < 3.2 From aa4f54e92e0b03c95991156f0308c2a6aa167116 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sun, 22 Nov 2020 18:08:11 +0800 Subject: [PATCH 059/156] Correct a typo in UVerb.hs (#1363) --- servant-server/src/Servant/Server/UVerb.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/src/Servant/Server/UVerb.hs b/servant-server/src/Servant/Server/UVerb.hs index bbddba34..f4096f5b 100644 --- a/servant-server/src/Servant/Server/UVerb.hs +++ b/servant-server/src/Servant/Server/UVerb.hs @@ -51,7 +51,7 @@ instance AllMime contentTypes, All (IsServerResource contentTypes) as, Unique (Statuses as) -- for consistency with servant-swagger (server would work fine - -- wihtout; client is a bit of a corner case, because it dispatches + -- without; client is a bit of a corner case, because it dispatches -- the parser based on the status code. with this uniqueness -- constraint it won't have to run more than one parser in weird -- corner cases. From ce638027a8d54130cee8a1a588931326c10b2a7a Mon Sep 17 00:00:00 2001 From: Andrey Prokopenko Date: Sun, 22 Nov 2020 13:08:32 +0300 Subject: [PATCH 060/156] Remove extra parameter from haddock section of Fragment instances (#1362) --- servant-client-core/src/Servant/Client/Core/HasClient.hs | 7 +++---- servant-server/src/Servant/Server/Internal.hs | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/servant-client-core/src/Servant/Client/Core/HasClient.hs b/servant-client-core/src/Servant/Client/Core/HasClient.hs index f9e087f5..a030a242 100644 --- a/servant-client-core/src/Servant/Client/Core/HasClient.hs +++ b/servant-client-core/src/Servant/Client/Core/HasClient.hs @@ -761,11 +761,10 @@ instance ( HasClient m api -- > myApi :: Proxy MyApi -- > myApi = Proxy -- > --- > getBooksBy :: Maybe Text -> ClientM [Book] --- > getBooksBy = client myApi +-- > getBooks :: ClientM [Book] +-- > getBooks = client myApi -- > -- then you can just use "getBooksBy" to query that endpoint. --- > -- 'getBooksBy Nothing' for all books --- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov +-- > -- 'getBooks' for all books. #ifdef HAS_TYPE_ERROR instance (AtLeastOneFragment api, FragmentUnique (Fragment a :> api), HasClient m api #else diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index 8ebfa9a1..9579367f 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -890,9 +890,9 @@ type HasServerArrowTypeError a b = -- > type MyApi = "books" :> Fragment Text :> Get '[JSON] [Book] -- > -- > server :: Server MyApi --- > server = getBooksBy --- > where getBooksBy :: Handler [Book] --- > getBooksBy = ...return all books... +-- > server = getBooks +-- > where getBooks :: Handler [Book] +-- > getBooks = ...return all books... #ifdef HAS_TYPE_ERROR instance (AtLeastOneFragment api, FragmentUnique (Fragment a1 :> api), HasServer api context) #else From 0ad2bd221ad390a442f740549a502d9524b2d5d5 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sun, 22 Nov 2020 17:51:32 +0300 Subject: [PATCH 061/156] Prepare 0.18.2 release (#1364) --- servant-client-core/CHANGELOG.md | 7 +++++++ servant-client-core/servant-client-core.cabal | 2 +- servant-client/CHANGELOG.md | 7 +++++++ servant-client/servant-client.cabal | 4 ++-- servant-docs/CHANGELOG.md | 7 +++++++ servant-docs/servant-docs.cabal | 2 +- servant-foreign/CHANGELOG.md | 12 ++++++++++++ servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/CHANGELOG.md | 7 +++++++ servant-http-streams/servant-http-streams.cabal | 4 ++-- servant-server/CHANGELOG.md | 7 +++++++ servant-server/servant-server.cabal | 4 ++-- servant/CHANGELOG.md | 8 ++++++++ servant/servant.cabal | 2 +- 14 files changed, 65 insertions(+), 10 deletions(-) diff --git a/servant-client-core/CHANGELOG.md b/servant-client-core/CHANGELOG.md index d1f6774f..3e34ac95 100644 --- a/servant-client-core/CHANGELOG.md +++ b/servant-client-core/CHANGELOG.md @@ -1,6 +1,13 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.2 +------ + +### Significant changes + +- Support `Fragment` combinator. + 0.18.1 ------ diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 201821b3..3420eaf2 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client-core -version: 0.18.1 +version: 0.18.2 synopsis: Core functionality and class for client function generation for servant APIs category: Servant, Web diff --git a/servant-client/CHANGELOG.md b/servant-client/CHANGELOG.md index b0a28654..f089ecf5 100644 --- a/servant-client/CHANGELOG.md +++ b/servant-client/CHANGELOG.md @@ -1,6 +1,13 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.2 +------ + +### Significant changes + +- Support `Fragment` combinator. + 0.18.1 ------ diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index e72b9cb4..768b3836 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client -version: 0.18.1 +version: 0.18.2 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -58,7 +58,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18.1 && <0.18.2 + , servant-client-core >= 0.18.2 && <0.18.3 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-docs/CHANGELOG.md b/servant-docs/CHANGELOG.md index be853dbe..47bc4388 100644 --- a/servant-docs/CHANGELOG.md +++ b/servant-docs/CHANGELOG.md @@ -1,6 +1,13 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-docs/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.11.8 +------ + +### Significant changes + +- Support `Fragment` combinator. + 0.11.7 ------ diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 27f2afa6..c2151ba4 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-docs -version: 0.11.7 +version: 0.11.8 synopsis: generate API docs for your servant webservice category: Servant, Web diff --git a/servant-foreign/CHANGELOG.md b/servant-foreign/CHANGELOG.md index c6bba80f..5f943dca 100644 --- a/servant-foreign/CHANGELOG.md +++ b/servant-foreign/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-foreign/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.15.3 +------ + +### Significant changes + +- Support `Fragment` combinator. + +0.15.2 +------ + +* Support `servant-0.18`. + 0.15.1 ------ diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 40e60438..5a821369 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-foreign -version: 0.15.2 +version: 0.15.3 synopsis: Helpers for generating clients for servant APIs in any programming language category: Servant, Web diff --git a/servant-http-streams/CHANGELOG.md b/servant-http-streams/CHANGELOG.md index 3d1e11ca..0d2d9f72 100644 --- a/servant-http-streams/CHANGELOG.md +++ b/servant-http-streams/CHANGELOG.md @@ -1,6 +1,13 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-http-streams/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.2 +------ + +### Significant changes + +- Support `servant-client-core` 0.18.2. + 0.18.1 ------ diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 68fbaf4c..62815dac 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-http-streams -version: 0.18.1 +version: 0.18.2 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -55,7 +55,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18.1 && <0.18.2 + , servant-client-core >= 0.18.2 && <0.18.3 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index 172d103f..43e03218 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -1,6 +1,13 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.2 +------ + +### Significant changes + +- Support `Fragment` combinator. + 0.18.1 ------ diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 4a4e40e4..660901c3 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-server -version: 0.18.1 +version: 0.18.2 synopsis: A family of combinators for defining webservices APIs and serving them category: Servant, Web @@ -71,7 +71,7 @@ library -- Servant dependencies -- strict dependency as we re-export 'servant' things. build-depends: - servant >= 0.18.1 && < 0.18.2 + servant >= 0.18.2 && < 0.18.3 , http-api-data >= 0.4.1 && < 0.4.3 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index 700afcfa..301081fb 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -1,5 +1,13 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.2 +------ + +### Significant changes + +- Introduce `Fragment` combinator. +- Fix `MimeRender` and `MimeUnrender` instances for `WithStatus`. + 0.18.1 ------ diff --git a/servant/servant.cabal b/servant/servant.cabal index 469f6cec..22f9dba7 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant -version: 0.18.1 +version: 0.18.2 synopsis: A family of combinators for defining webservices APIs category: Servant, Web From 57badc7c748edf5e47d53533001be3b71cce45ec Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sun, 22 Nov 2020 18:08:01 +0300 Subject: [PATCH 062/156] Add UVerb cookbook to cookbook build (#1365) --- doc/cookbook/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/cookbook/index.rst b/doc/cookbook/index.rst index acd9efe3..9d4601a7 100644 --- a/doc/cookbook/index.rst +++ b/doc/cookbook/index.rst @@ -26,6 +26,7 @@ you name it! 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 From f7dc40ca8dbd31d45d70d8acc223879d6b22b152 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Wed, 25 Nov 2020 13:06:45 +0100 Subject: [PATCH 063/156] servant-client-core: depend on 0.18.2 (#1366) --- servant-client-core/servant-client-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 3420eaf2..41e86183 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -64,7 +64,7 @@ library -- Servant dependencies build-depends: - servant >= 0.18.1 && <0.19 + servant >= 0.18.2 && <0.19 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. From 0bda65e315587689e99fc5f49eaeb627c66786e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= Date: Sat, 5 Dec 2020 17:00:03 +0100 Subject: [PATCH 064/156] links: import toUrlPiece to make it clear where it comes from --- servant/src/Servant/Links.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/servant/src/Servant/Links.hs b/servant/src/Servant/Links.hs index 39a228d7..cdafb310 100644 --- a/servant/src/Servant/Links.hs +++ b/servant/src/Servant/Links.hs @@ -17,6 +17,7 @@ -- >>> :set -XDataKinds -XTypeFamilies -XTypeOperators -- >>> import Servant.API -- >>> import Servant.Links +-- >>> import Web.HttpApiData (toUrlPiece) -- >>> import Data.Proxy -- >>> -- >>> type Hello = "hello" :> Get '[JSON] Int From 08579ca0039410e04d6c36c975ddc20165819db6 Mon Sep 17 00:00:00 2001 From: Alexey Kuleshevich Date: Sat, 5 Dec 2020 22:49:11 +0300 Subject: [PATCH 065/156] Update upper bounds for QuickCheck (#1375) --- servant-client-core/servant-client-core.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 41e86183..92ef8b75 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -104,7 +104,7 @@ test-suite spec build-depends: deepseq >= 1.4.2.0 && < 1.5 , hspec >= 2.6.0 && < 2.8 - , QuickCheck >= 2.12.6.1 && < 2.14 + , QuickCheck >= 2.12.6.1 && < 2.15 build-tool-depends: hspec-discover:hspec-discover >= 2.6.0 && <2.8 diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 768b3836..bfb66c18 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -122,7 +122,7 @@ test-suite spec , hspec >= 2.6.0 && < 2.8 , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 - , QuickCheck >= 2.12.6.1 && < 2.14 + , QuickCheck >= 2.12.6.1 && < 2.15 , servant == 0.18.* , servant-server == 0.18.* , tdigest >= 0.2 && < 0.3 diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 62815dac..a2d67af6 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -115,7 +115,7 @@ test-suite spec , hspec >= 2.6.0 && < 2.8 , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 - , QuickCheck >= 2.12.6.1 && < 2.14 + , QuickCheck >= 2.12.6.1 && < 2.15 , servant == 0.18.* , servant-server == 0.18.* , tdigest >= 0.2 && < 0.3 From a8f584f80b6b5169ed044da9000ca22f349b4038 Mon Sep 17 00:00:00 2001 From: Intolerable Date: Sun, 6 Dec 2020 13:19:35 +0000 Subject: [PATCH 066/156] add HasLink instance for UVerb (#1370) --- servant/src/Servant/Links.hs | 6 ++++++ servant/test/Servant/LinksSpec.hs | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/servant/src/Servant/Links.hs b/servant/src/Servant/Links.hs index cdafb310..bfd47206 100644 --- a/servant/src/Servant/Links.hs +++ b/servant/src/Servant/Links.hs @@ -178,6 +178,7 @@ import Servant.API.Stream import Servant.API.Sub (type (:>)) import Servant.API.TypeLevel +import Servant.API.UVerb import Servant.API.Vault (Vault) import Servant.API.Verbs @@ -576,6 +577,11 @@ instance HasLink (Stream m status fr ct a) where type MkLink (Stream m status fr ct a) r = r toLink toA _ = toA +-- UVerb instances +instance HasLink (UVerb m ct a) where + type MkLink (UVerb m ct a) r = r + toLink toA _ = toA + -- AuthProtext instances instance HasLink sub => HasLink (AuthProtect tag :> sub) where type MkLink (AuthProtect tag :> sub) a = MkLink sub a diff --git a/servant/test/Servant/LinksSpec.hs b/servant/test/Servant/LinksSpec.hs index 9d45e4a9..55c68228 100644 --- a/servant/test/Servant/LinksSpec.hs +++ b/servant/test/Servant/LinksSpec.hs @@ -29,6 +29,9 @@ type TestApi = -- Fragment :<|> "say" :> Fragment String :> Get '[JSON] NoContent + -- UVerb + :<|> "uverb-example" :> UVerb 'GET '[JSON] '[WithStatus 200 NoContent] + -- All of the verbs :<|> "get" :> Get '[JSON] NoContent :<|> "put" :> Put '[JSON] NoContent @@ -73,6 +76,10 @@ spec = describe "Servant.Links" $ do ["roads", "lead", "to", "rome"] `shouldBeLink` "all/roads/lead/to/rome" + it "generated correct links for UVerbs" $ do + apiLink (Proxy :: Proxy ("uverb-example" :> UVerb 'GET '[JSON] '[WithStatus 200 NoContent])) + `shouldBeLink` "uverb-example" + it "generates correct links for query flags" $ do let l1 = Proxy :: Proxy ("balls" :> QueryFlag "bouncy" :> QueryFlag "fast" :> Delete '[JSON] NoContent) From 2f20c3270404e4b8c0b1b0d3662ee85124d66258 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Sun, 6 Dec 2020 16:03:19 +0100 Subject: [PATCH 067/156] Don't warn about necessary, expected type errors. --- servant-server/test/Servant/Server/Internal/ContextSpec.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/test/Servant/Server/Internal/ContextSpec.hs b/servant-server/test/Servant/Server/Internal/ContextSpec.hs index 93e1dcb8..0132f396 100644 --- a/servant-server/test/Servant/Server/Internal/ContextSpec.hs +++ b/servant-server/test/Servant/Server/Internal/ContextSpec.hs @@ -1,5 +1,5 @@ {-# LANGUAGE DataKinds #-} -{-# OPTIONS_GHC -fdefer-type-errors -Wwarn #-} +{-# OPTIONS_GHC -fdefer-type-errors -Wwarn -Wno-deferred-type-errors #-} module Servant.Server.Internal.ContextSpec (spec) where import Data.Proxy From fe849b27bf2a6ec1200d843820b036a65aaad776 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Sun, 6 Dec 2020 16:04:02 +0100 Subject: [PATCH 068/156] bump stack.yaml resolver. --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 491c55b1..def7c40e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: lts-14.17 +resolver: lts-16.24 packages: - servant-client-core/ - servant-client/ From 6ebb9e419e89c1bd65512ddc2899d27724679005 Mon Sep 17 00:00:00 2001 From: fisx Date: Wed, 9 Dec 2020 23:08:54 +0100 Subject: [PATCH 069/156] Fix overlapping MimeRender instances (#1376) --- servant/src/Servant/API/UVerb.hs | 33 ++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/servant/src/Servant/API/UVerb.hs b/servant/src/Servant/API/UVerb.hs index f7b1f740..ed59eded 100644 --- a/servant/src/Servant/API/UVerb.hs +++ b/servant/src/Servant/API/UVerb.hs @@ -36,7 +36,7 @@ where import Data.Proxy (Proxy (Proxy)) import GHC.TypeLits (Nat) import Network.HTTP.Types (Status, StdMethod) -import Servant.API.ContentTypes (NoContent, MimeRender(mimeRender), MimeUnrender(mimeUnrender)) +import Servant.API.ContentTypes (JSON, PlainText, FormUrlEncoded, OctetStream, NoContent, MimeRender(mimeRender), MimeUnrender(mimeUnrender)) import Servant.API.Status (KnownStatus, statusVal) import Servant.API.UVerb.Union @@ -69,13 +69,6 @@ instance (HasStatus a, HasStatuses as) => HasStatuses (a ': as) where newtype WithStatus (k :: Nat) a = WithStatus a deriving (Eq, Show) -instance MimeRender ctype a => MimeRender ctype (WithStatus _status a) where - mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a - -instance MimeUnrender ctype a => MimeUnrender ctype (WithStatus _status a) where - mimeUnrender contentTypeProxy input = - WithStatus <$> mimeUnrender contentTypeProxy input - -- | an instance of this typeclass assigns a HTTP status code to a return type -- -- Example: @@ -105,3 +98,27 @@ instance KnownStatus n => HasStatus (WithStatus n a) where -- Backwards compatibility is tricky, though: this type alias would mean people would have to -- use 'respond' instead of 'pure' or 'return', so all old handlers would have to be rewritten. data UVerb (method :: StdMethod) (contentTypes :: [*]) (as :: [*]) + +instance {-# OVERLAPPING #-} MimeRender JSON a => MimeRender JSON (WithStatus _status a) where + mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a + +instance {-# OVERLAPPING #-} MimeRender PlainText a => MimeRender PlainText (WithStatus _status a) where + mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a + +instance {-# OVERLAPPING #-} MimeRender FormUrlEncoded a => MimeRender FormUrlEncoded (WithStatus _status a) where + mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a + +instance {-# OVERLAPPING #-} MimeRender OctetStream a => MimeRender OctetStream (WithStatus _status a) where + mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a + +instance {-# OVERLAPPING #-} MimeUnrender JSON a => MimeUnrender JSON (WithStatus _status a) where + mimeUnrender contentTypeProxy input = WithStatus <$> mimeUnrender contentTypeProxy input + +instance {-# OVERLAPPING #-} MimeUnrender PlainText a => MimeUnrender PlainText (WithStatus _status a) where + mimeUnrender contentTypeProxy input = WithStatus <$> mimeUnrender contentTypeProxy input + +instance {-# OVERLAPPING #-} MimeUnrender FormUrlEncoded a => MimeUnrender FormUrlEncoded (WithStatus _status a) where + mimeUnrender contentTypeProxy input = WithStatus <$> mimeUnrender contentTypeProxy input + +instance {-# OVERLAPPING #-} MimeUnrender OctetStream a => MimeUnrender OctetStream (WithStatus _status a) where + mimeUnrender contentTypeProxy input = WithStatus <$> mimeUnrender contentTypeProxy input From 7675e725d2fd546d87cf3927d10591182fa5bb5e Mon Sep 17 00:00:00 2001 From: Philipp Balzarek Date: Fri, 11 Dec 2020 00:32:16 +0100 Subject: [PATCH 070/156] Bump base64-bytestring limit to 1.3 (#1382) --- servant-server/servant-server.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 660901c3..4a19d9eb 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -78,7 +78,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.12 - , base64-bytestring >= 1.0.0.1 && < 1.2 + , base64-bytestring >= 1.0.0.1 && < 1.3 , exceptions >= 0.10.0 && < 0.11 , http-media >= 0.7.1.3 && < 0.9 , http-types >= 0.12.2 && < 0.13 From 7412ac34726bfb8393bdda02debe6be2ca97f032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20S=C3=BAkup?= Date: Fri, 11 Dec 2020 21:15:27 +0100 Subject: [PATCH 071/156] Update upper bound limit of base64-bytestring (#1383) --- servant-client-core/servant-client-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 92ef8b75..2b6064e8 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -71,7 +71,7 @@ library build-depends: aeson >= 1.4.1.0 && < 1.6 , base-compat >= 0.10.5 && < 0.12 - , base64-bytestring >= 1.0.0.1 && < 1.2 + , base64-bytestring >= 1.0.0.1 && < 1.3 , exceptions >= 0.10.0 && < 0.11 , free >= 5.1 && < 5.2 , http-media >= 0.7.1.3 && < 0.9 From 1f701aa97d8da0a00c53535f1fc6e012fe2914c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20S=C3=BAkup?= Date: Fri, 11 Dec 2020 22:52:32 +0100 Subject: [PATCH 072/156] Update upper bound limit for http-client (#1384) --- servant-client/servant-client.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index bfb66c18..a52c04a7 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -64,7 +64,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.12 - , http-client >= 0.5.13.1 && < 0.7 + , http-client >= 0.5.13.1 && < 0.8 , http-media >= 0.7.1.3 && < 0.9 , http-types >= 0.12.2 && < 0.13 , exceptions >= 0.10.0 && < 0.11 From 27173c922311112dd153346cf3cd72b9fb0f3551 Mon Sep 17 00:00:00 2001 From: Bodigrim Date: Wed, 16 Dec 2020 10:04:49 +0000 Subject: [PATCH 073/156] Allow bytestring-0.11 (#1386) --- servant-client-core/servant-client-core.cabal | 2 +- servant-client-ghcjs/servant-client-ghcjs.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-conduit/servant-conduit.cabal | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-machines/servant-machines.cabal | 2 +- servant-pipes/servant-pipes.cabal | 2 +- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 2b6064e8..9c99f10c 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -51,7 +51,7 @@ library -- note: mtl lower bound is so low because of GHC-7.8 build-depends: base >= 4.9 && < 4.15 - , bytestring >= 0.10.8.1 && < 0.11 + , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 , text >= 1.2.3.0 && < 1.3 diff --git a/servant-client-ghcjs/servant-client-ghcjs.cabal b/servant-client-ghcjs/servant-client-ghcjs.cabal index 5308525e..639fc7fc 100644 --- a/servant-client-ghcjs/servant-client-ghcjs.cabal +++ b/servant-client-ghcjs/servant-client-ghcjs.cabal @@ -39,7 +39,7 @@ library build-depends: base >=4.11 && <4.12 - , bytestring >=0.10 && <0.11 + , bytestring >=0.10 && <0.12 , case-insensitive >=1.2.0.0 && <1.3.0.0 , containers >=0.5 && <0.7 , exceptions >=0.8 && <0.11 diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index a52c04a7..9f6a6f64 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -41,7 +41,7 @@ library -- text and mtl are bundled starting with GHC-8.4 build-depends: base >= 4.9 && < 4.15 - , bytestring >= 0.10.8.1 && < 0.11 + , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 , mtl >= 2.2.2 && < 2.3 diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 98e9c3ef..53e50bc9 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -29,7 +29,7 @@ library exposed-modules: Servant.Conduit build-depends: base >=4.9 && <5 - , bytestring >=0.10.8.1 && <0.11 + , bytestring >=0.10.8.1 && <0.12 , conduit >=1.3.1 && <1.4 , mtl >=2.2.2 && <2.3 , resourcet >=1.2.2 && <1.3 diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index c2151ba4..d2c8623e 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -42,7 +42,7 @@ library -- note: mtl lower bound is so low because of GHC-7.8 build-depends: base >= 4.9 && < 4.15 - , bytestring >= 0.10.8.1 && < 0.11 + , bytestring >= 0.10.8.1 && < 0.12 , text >= 1.2.3.0 && < 1.3 -- Servant dependencies diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index a2d67af6..055dc3d5 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -39,7 +39,7 @@ library -- text and mtl are bundled starting with GHC-8.4 build-depends: base >= 4.9 && < 4.15 - , bytestring >= 0.10.8.1 && < 0.11 + , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 , mtl >= 2.2.2 && < 2.3 diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index f49ea4d9..2ce1f564 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -29,7 +29,7 @@ library exposed-modules: Servant.Machines build-depends: base >=4.9 && <5 - , bytestring >=0.10.8.1 && <0.11 + , bytestring >=0.10.8.1 && <0.12 , machines >=0.6.4 && <0.8 , mtl >=2.2.2 && <2.3 , servant >=0.15 && <0.19 diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 9e25bfbe..801f5850 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -29,7 +29,7 @@ library exposed-modules: Servant.Pipes build-depends: base >=4.9 && <5 - , bytestring >=0.10.8.1 && <0.11 + , bytestring >=0.10.8.1 && <0.12 , pipes >=4.3.9 && <4.4 , pipes-safe >=2.3.1 && <2.4 , mtl >=2.2.2 && <2.3 diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 4a19d9eb..f4833943 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -61,7 +61,7 @@ library -- text and mtl are bundled starting with GHC-8.4 build-depends: base >= 4.9 && < 4.15 - , bytestring >= 0.10.8.1 && < 0.11 + , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , mtl >= 2.2.2 && < 2.3 , text >= 1.2.3.0 && < 1.3 diff --git a/servant/servant.cabal b/servant/servant.cabal index 22f9dba7..260e9113 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -79,7 +79,7 @@ library -- note: mtl lower bound is so low because of GHC-7.8 build-depends: base >= 4.9 && < 4.15 - , bytestring >= 0.10.8.1 && < 0.11 + , bytestring >= 0.10.8.1 && < 0.12 , mtl >= 2.2.2 && < 2.3 , sop-core >= 0.4.0.0 && < 0.6 , transformers >= 0.5.2.0 && < 0.6 From f1b5a644669420bc5384fd76221766fe9c1fbb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Sun, 14 Mar 2021 21:34:36 +0100 Subject: [PATCH 074/156] Remove Travis / haskell-ci configuration Also remove mention about re-generating .travis.yml in README --- .travis.yml | 432 ----------------------------------------------- README.md | 20 --- cabal.haskell-ci | 25 --- 3 files changed, 477 deletions(-) delete mode 100644 .travis.yml delete mode 100644 cabal.haskell-ci diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 79214952..00000000 --- a/.travis.yml +++ /dev/null @@ -1,432 +0,0 @@ -# This Travis job script has been generated by a script via -# -# haskell-ci '--branches' 'master' '--output' '.travis.yml' '--config=cabal.haskell-ci' 'cabal.project' -# -# To regenerate the script (for example after adjusting tested-with) run -# -# haskell-ci regenerate -# -# For more information, see https://github.com/haskell-CI/haskell-ci -# -# version: 0.10.3 -# -version: ~> 1.0 -language: c -os: linux -dist: bionic -git: - # whether to recursively clone submodules - submodules: false -branches: - only: - - master - - 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.10.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.10.2","cabal-install-3.2"]}} - os: linux - - compiler: ghc-8.10.1 - 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.10.1","cabal-install-3.2"]}} - os: linux - - compiler: ghc-8.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"}],"packages":["ghc-8.8.4","cabal-install-3.2"]}} - os: linux - - compiler: ghc-8.8.3 - 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.3","cabal-install-3.2"]}} - 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.2"]}} - 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.2"]}} - 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.2"]}} - 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.2"]}} - 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 ${CABAL} v2-install $WITHCOMPILER --ignore-project -j2 doctest --constraint='doctest ^>=0.17' ; fi - - if $GHCJS ; then ${CABAL} v2-install -w ghc-8.4.4 --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' ; fi - - if $GHCJS ; then ${CABAL} v2-install -w ghc-8.4.4 --ignore-project hspec-discover ; fi - # Generate cabal.project - - rm -rf cabal.project cabal.project.local cabal.project.freeze - - touch cabal.project - - | - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-client" >> cabal.project ; fi - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-client-core" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-http-streams" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-docs" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-foreign" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-server" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/tutorial" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-machines" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-conduit" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: servant-pipes" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/basic-auth" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/curl-mock" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/custom-errors" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/basic-streaming" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/db-postgres-pool" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/file-upload" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/generic" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/pagination" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/testing" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/uverb" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/structuring-apis" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-custom-monad" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-free-client" >> cabal.project ; fi - - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client-core' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-http-streams' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-docs' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-foreign' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-server' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package tutorial' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-machines' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-conduit' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-pipes' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-file-upload' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-generic' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-pagination' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-uverb' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> 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 "allow-newer: servant-multipart:servant" >> cabal.project - echo "allow-newer: servant-multipart:servant-server" >> cabal.project - echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project - echo "allow-newer: servant-js:servant" >> 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-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|cookbook-uverb|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/custom-errors/configure.ac" ]; then (cd "doc/cookbook/custom-errors" && 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/uverb/configure.ac" ]; then (cd "doc/cookbook/uverb" && 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_custom_errors="$(find . -maxdepth 1 -type d -regex '.*/cookbook-custom-errors-[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_uverb="$(find . -maxdepth 1 -type d -regex '.*/cookbook-uverb-[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 - - | - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_client}" >> cabal.project ; fi - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_client_core}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_http_streams}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_docs}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_foreign}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_server}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_tutorial}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_machines}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_conduit}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo "packages: ${PKGDIR_servant_pipes}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_basic_auth}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_curl_mock}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_custom_errors}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_basic_streaming}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_db_postgres_pool}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_file_upload}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_generic}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_pagination}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_testing}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_uverb}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_structuring_apis}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_custom_monad}" >> cabal.project ; fi - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_free_client}" >> cabal.project ; fi - - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-client-core' >> cabal.project ; fi - - "if $GHCJS || ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-http-streams' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-docs' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-foreign' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-server' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package tutorial' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-machines' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-conduit' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo 'package servant-pipes' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80200 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-auth' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-curl-mock' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-custom-errors' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-basic-streaming' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-db-postgres-pool' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-file-upload' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-generic' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-pagination' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-uverb' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi" - - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-free-client' >> cabal.project ; fi - - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> 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 "allow-newer: servant-multipart:servant" >> cabal.project - echo "allow-newer: servant-multipart:servant-server" >> cabal.project - echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project - echo "allow-newer: servant-js:servant" >> 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-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|cookbook-uverb|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-custom-errors) echo ${PKGDIR_cookbook_custom_errors} ;; - 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-uverb) echo ${PKGDIR_cookbook_uverb} ;; - 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 () { print unless /package-id\s+(base-compat-batteries)-\d+(\.\d+)*/; }' .ghc.environment.* - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_client} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_client_core} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_http_streams} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_docs} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_foreign} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_server} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_machines} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; then (cd ${PKGDIR_servant_conduit} && doctest src) ; fi - - if ! $GHCJS && { [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81000 ] || [ $HCNUMVER -ge 81002 ]; } ; 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.10.3",["--branches","master","--output",".travis.yml","--config=cabal.haskell-ci","cabal.project"]) -# EOF diff --git a/README.md b/README.md index 6310638c..0d06a4e8 100644 --- a/README.md +++ b/README.md @@ -40,26 +40,6 @@ See `CONTRIBUTING.md` - `git push --tags` - `cabal sdist` and `cabal upload` -## travis - -`.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 to a dependency failing to build, you can temporarily -add `constraints` to the `cabal.project` file, and regenerate the `.travis.yml`. -For example, the following will disallow a single `troublemaker-13.37` package version: - -``` -constraints: - troublemaker <13.37 && > 13.37 -``` - ## TechEmpower framework benchmarks We develop and maintain the servant TFB entry in https://github.com/haskell-servant/FrameworkBenchmarks/ diff --git a/cabal.haskell-ci b/cabal.haskell-ci deleted file mode 100644 index ac041d53..00000000 --- a/cabal.haskell-ci +++ /dev/null @@ -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 From 613dcf9ed5d567f8a16f3dfc47dd0afa768871e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Tue, 2 Mar 2021 21:46:04 +0100 Subject: [PATCH 075/156] 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. --- .github/workflows/main.yml | 95 +++++++++++++++++++ cabal.project | 6 +- .../test/Servant/ClientSpec.hs | 2 +- 3 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..43c6e7f7 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,95 @@ +name: CI + +# Trigger the workflow on push or pull request, but only for the main branch +on: + pull_request: + push: + branches: [master] + +jobs: + cabal: + name: ${{ matrix.os }} / ghc ${{ matrix.ghc }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macOS-latest] + cabal: ["3.2"] + ghc: + - "8.8.4" + - "8.10.2" + # exclude: + # - os: macOS-latest + # ghc: 8.8.4 + # - os: macOS-latest + # ghc: 8.6.5 + # - os: windows-latest + # ghc: 8.10.2 + # - os: windows-latest + # ghc: 8.6.5 + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-haskell@v1.1.4 + id: setup-haskell-cabal + name: Setup Haskell + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: ${{ matrix.cabal }} + + - name: Configure + run: | + cabal configure --enable-tests --enable-benchmarks --test-show-details=direct + + - name: Freeze + run: | + cabal freeze + + - uses: actions/cache@v2.1.3 + name: Cache ~/.cabal/store + with: + path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} + key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} + + - name: Build + run: | + cabal build all + + - name: Test + run: | + cabal test all + + stack: + name: stack / ghc ${{ matrix.ghc }} + runs-on: ubuntu-latest + strategy: + matrix: + stack: ["2.3.1"] + ghc: ["8.8.4"] + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-haskell@v1.1.4 + 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 diff --git a/cabal.project b/cabal.project index 21145d96..b8516e66 100644 --- a/cabal.project +++ b/cabal.project @@ -3,7 +3,8 @@ packages: servant-client/ servant-client-core/ servant-http-streams/ - servant-docs/ +-- Tests failing with Cabal (TODO: investigate) +-- servant-docs/ servant-foreign/ servant-server/ doc/tutorial/ @@ -33,7 +34,8 @@ packages: -- 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 diff --git a/servant-http-streams/test/Servant/ClientSpec.hs b/servant-http-streams/test/Servant/ClientSpec.hs index 14efd88f..41e7fbe4 100644 --- a/servant-http-streams/test/Servant/ClientSpec.hs +++ b/servant-http-streams/test/Servant/ClientSpec.hs @@ -482,7 +482,7 @@ startWaiApp app = do (port, socket) <- openTestSocket let settings = setPort port $ defaultSettings thread <- forkIO $ runSettingsSocket settings socket app - return (thread, BaseUrl Http "localhost" port "") + return (thread, BaseUrl Http "127.0.0.1" port "") endWaiApp :: (ThreadId, BaseUrl) -> IO () From 133ed944422cf8ee1d0cef5f7d8283347d4c0391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Tue, 16 Mar 2021 10:23:23 +0100 Subject: [PATCH 076/156] Re-enable doctests on CI --- .github/workflows/main.yml | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43c6e7f7..4f93eeb4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macOS-latest] + os: [ubuntu-latest] cabal: ["3.2"] ghc: - "8.8.4" @@ -40,6 +40,7 @@ jobs: - name: Configure run: | cabal configure --enable-tests --enable-benchmarks --test-show-details=direct + cabal install --ignore-project -j2 doctest --constraint='doctest ^>=0.17' - name: Freeze run: | @@ -59,6 +60,30 @@ jobs: run: | cabal test all + - name: Run doctests + run: | + # Necessary for doctest to be found in $PATH + export PATH="$HOME/.cabal/bin:$PATH" + + # Filter out base-compat-batteries from .ghc.environment.*, as its modules + # conflict with those of base-compat. + # + # FIXME: This is an ugly hack. Ultimately, we'll want to use cabal-doctest + # (or cabal v2-doctest, if it ever lands) to provide a clean GHC environment. + # This might allow running doctests in GHCJS build as well. + perl -i -e 'while () { print unless /package-id\s+(base-compat-batteries)-\d+(\.\d+)*/; }' .ghc.environment.* + + (cd servant && doctest src) + (cd servant-client && doctest src) + (cd servant-client-core && doctest src) + (cd servant-http-streams && doctest src) + (cd servant-docs && doctest src) + (cd servant-foreign && doctest src) + (cd servant-server && doctest src) + (cd servant-machines && doctest src) + (cd servant-conduit && doctest src) + (cd servant-pipes && doctest src) + stack: name: stack / ghc ${{ matrix.ghc }} runs-on: ubuntu-latest From 86eb25018e29c74b25b9244c8a057372ec1854f4 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 16 Mar 2021 19:55:01 +0100 Subject: [PATCH 077/156] removing DerivingVia extension (not compatible ghc < 8.6.1) --- doc/cookbook/uverb/UVerb.lhs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/cookbook/uverb/UVerb.lhs b/doc/cookbook/uverb/UVerb.lhs index e5e9bc07..d281a383 100644 --- a/doc/cookbook/uverb/UVerb.lhs +++ b/doc/cookbook/uverb/UVerb.lhs @@ -12,7 +12,6 @@ handlers that respond with arbitrary open unions of types. {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} @@ -158,7 +157,9 @@ Example usage: ```haskell data Foo = Foo Int Int Int deriving (Show, Eq, GHC.Generic, ToJSON) - deriving HasStatus via WithStatus 200 Foo + +instance HasStatus Foo where + type StatusOf Foo = 200 data Bar = Bar deriving (Show, Eq, GHC.Generic, ToJSON) From 08b5e8653683a6558aa39898684b91819438fbcf Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 16 Mar 2021 20:31:41 +0100 Subject: [PATCH 078/156] (<>) needs the import of Data.Semigroup (ghc 8.2.2) --- doc/cookbook/custom-errors/CustomErrors.lhs | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/cookbook/custom-errors/CustomErrors.lhs b/doc/cookbook/custom-errors/CustomErrors.lhs index 4e8b773c..a8330a5e 100644 --- a/doc/cookbook/custom-errors/CustomErrors.lhs +++ b/doc/cookbook/custom-errors/CustomErrors.lhs @@ -20,6 +20,7 @@ Extensions and imports: import Data.Aeson import Data.Proxy import Data.Text +import Data.Semigroup import GHC.Generics import Network.Wai import Network.Wai.Handler.Warp From 9357583459f6d406dc80d23d61115ef2345a9877 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 16 Mar 2021 21:03:09 +0100 Subject: [PATCH 079/156] removing DerivingStrategies extension (not compatible ghc < 8.2.1) --- doc/cookbook/uverb/UVerb.lhs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/cookbook/uverb/UVerb.lhs b/doc/cookbook/uverb/UVerb.lhs index d281a383..2119706c 100644 --- a/doc/cookbook/uverb/UVerb.lhs +++ b/doc/cookbook/uverb/UVerb.lhs @@ -9,9 +9,7 @@ handlers that respond with arbitrary open unions of types. ```haskell {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} -{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} @@ -133,7 +131,7 @@ Since `UVerb` (probably) will mostly be used for error-like responses, it may be ```haskell newtype UVerbT xs m a = UVerbT { unUVerbT :: ExceptT (Union xs) m a } - deriving newtype (Functor, Applicative, Monad, MonadTrans) + deriving (Functor, Applicative, Monad, MonadTrans) -- | Deliberately hide 'ExceptT's 'MonadError' instance to be able to use -- underlying monad's instance. @@ -156,13 +154,17 @@ Example usage: ```haskell data Foo = Foo Int Int Int - deriving (Show, Eq, GHC.Generic, ToJSON) + deriving (Show, Eq, GHC.Generic) + +instance ToJSON Foo instance HasStatus Foo where type StatusOf Foo = 200 data Bar = Bar - deriving (Show, Eq, GHC.Generic, ToJSON) + deriving (Show, Eq, GHC.Generic) + +instance ToJSON Bar h :: Handler (Union '[Foo, WithStatus 400 Bar]) h = runUVerbT $ do From f9dd1f691fb727962c9921ca703d8c63383a5e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Wed, 17 Mar 2021 10:28:04 +0100 Subject: [PATCH 080/156] Try installing GHCJS via HVR's PPA --- .github/workflows/main.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4f93eeb4..36701bf3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -118,3 +118,16 @@ jobs: - name: Test run: | stack test --system-ghc + + ghcjs: + name: stack / ghcjs 8.4 + runs-on: "ubuntu-18.04" + + steps: + - uses: actions/checkout@v2 + + - name: Install ghcjs + run: | + sudo add-apt-repository ppa:hvr/ghcjs + sudo apt-get update -y + sudo apt-get install ghcjs-8.4 From 579a372eb908328f8a326c7b65d1fb28faadcfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Tue, 16 Mar 2021 18:32:21 +0100 Subject: [PATCH 081/156] Enable all tested-with GHC versions --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 36701bf3..7c97895c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,10 @@ jobs: os: [ubuntu-latest] cabal: ["3.2"] ghc: + - "8.0.2" + - "8.2.2" + - "8.4.4" + - "8.6.5" - "8.8.4" - "8.10.2" # exclude: From 95d4f5030f1cc1b078d26843b789ca3c13f2c8a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Wed, 17 Mar 2021 12:11:03 +0100 Subject: [PATCH 082/156] Build / tests with GHCJS --- .github/workflows/main.yml | 27 ++++++++++++++++++++++++++- cabal.ghcjs.project | 1 - 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7c97895c..68c5d6ef 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -130,8 +130,33 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Install ghcjs + - name: Install ghcjs and tools run: | sudo add-apt-repository ppa:hvr/ghcjs sudo apt-get update -y sudo apt-get install ghcjs-8.4 + sudo apt-get install cabal-install + cabal --version + # Override cabal.project with the lightweight GHCJS one + cp cabal.ghcjs.project cabal.project + cat cabal.project + + export PATH=/opt/ghcjs/8.4.4/bin:$PATH + export PATH="$HOME/.cabal/bin:$PATH" + cabal v2-update + ls /opt + ls /opt/ghcjs/8.4/bin + # cabal v2-install --ghcjs -w /opt/ghcjs/8.4/bin/ghcjs --ignore-project hspec-discover + cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' + cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project hspec-discover + + - name: Build + run: | + export PATH=/opt/ghcjs/8.4.4/bin:$PATH + export PATH="$HOME/.cabal/bin:$PATH" + cabal v2-build --ghcjs -w /opt/ghcjs/8.4/bin/ghcjs --enable-tests --enable-benchmarks all + + - name: Run tests + run: | + export PATH="$HOME/.cabal/bin:$PATH" + 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 diff --git a/cabal.ghcjs.project b/cabal.ghcjs.project index d7f2c49f..11ceb957 100644 --- a/cabal.ghcjs.project +++ b/cabal.ghcjs.project @@ -3,7 +3,6 @@ packages: servant/ servant-client-core/ - servant-jsaddle/ -- we need to tell cabal we are using GHCJS compiler: ghcjs From 6452942a6942dd44d29e126d57992749ca188bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Wed, 17 Mar 2021 21:32:04 +0100 Subject: [PATCH 083/156] Cleanup --- .github/workflows/main.yml | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 68c5d6ef..63f3c1d2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: CI -# Trigger the workflow on push or pull request, but only for the main branch +# Trigger the workflow on push or pull request, but only for the master branch on: pull_request: push: @@ -21,15 +21,6 @@ jobs: - "8.6.5" - "8.8.4" - "8.10.2" - # exclude: - # - os: macOS-latest - # ghc: 8.8.4 - # - os: macOS-latest - # ghc: 8.6.5 - # - os: windows-latest - # ghc: 8.10.2 - # - os: windows-latest - # ghc: 8.6.5 steps: - uses: actions/checkout@v2 @@ -124,39 +115,38 @@ jobs: stack test --system-ghc ghcjs: - name: stack / ghcjs 8.4 + name: cabal / ghcjs 8.4 runs-on: "ubuntu-18.04" steps: - uses: actions/checkout@v2 + - name: "Setup PATH" + run: | + echo "PATH=$HOME/.cabal/bin:$PATH" >> $GITHUB_ENV - - name: Install ghcjs and tools + - name: Install tools run: | sudo add-apt-repository ppa:hvr/ghcjs sudo apt-get update -y sudo apt-get install ghcjs-8.4 sudo apt-get install cabal-install - cabal --version + # Override cabal.project with the lightweight GHCJS one cp cabal.ghcjs.project cabal.project cat cabal.project - export PATH=/opt/ghcjs/8.4.4/bin:$PATH - export PATH="$HOME/.cabal/bin:$PATH" cabal v2-update - ls /opt - ls /opt/ghcjs/8.4/bin - # cabal v2-install --ghcjs -w /opt/ghcjs/8.4/bin/ghcjs --ignore-project hspec-discover cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project hspec-discover - name: Build run: | - export PATH=/opt/ghcjs/8.4.4/bin:$PATH - export PATH="$HOME/.cabal/bin:$PATH" cabal v2-build --ghcjs -w /opt/ghcjs/8.4/bin/ghcjs --enable-tests --enable-benchmarks all - name: Run tests run: | - export PATH="$HOME/.cabal/bin:$PATH" + # 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 "$(pkgdir $testpkg)" && nodejs "$testexe".jsexe/all.js); done From 507990cafe8aac157b1001732816accc707823f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Wed, 17 Mar 2021 23:14:56 +0100 Subject: [PATCH 084/156] Switch to actively maintained haskell/actions/setup for CI --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 63f3c1d2..852ffdd6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/setup-haskell@v1.1.4 + - uses: haskell/actions/setup@v1 id: setup-haskell-cabal name: Setup Haskell with: @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/setup-haskell@v1.1.4 + - uses: haskell/actions/setup@v1 name: Setup Haskell Stack with: ghc-version: ${{ matrix.ghc }} From dd1ab6dd3668f6aad419534ba9663043cdfe429d Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Fri, 5 Mar 2021 08:16:35 +0100 Subject: [PATCH 085/156] fixing DocSpec tests to make them compile --- .gitignore | 1 + servant-docs/test/Servant/DocsSpec.hs | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a74ddee2..6cec8e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ doc/_build doc/venv doc/tutorial/static/api.js doc/tutorial/static/jq.js +shell.nix # nix result* diff --git a/servant-docs/test/Servant/DocsSpec.hs b/servant-docs/test/Servant/DocsSpec.hs index 5da5ff4d..357beed8 100644 --- a/servant-docs/test/Servant/DocsSpec.hs +++ b/servant-docs/test/Servant/DocsSpec.hs @@ -126,8 +126,11 @@ spec = describe "Servant.Docs" $ do it "mentions headers" $ do md `shouldContain` "- This endpoint is sensitive to the value of the **X-Test** HTTP header." - it "contains response samples" $ - md `shouldContain` "{\"dt1field1\":\"field 1\",\"dt1field2\":13}" + it "contains response samples - dt1field1" $ + md `shouldContain` "\"dt1field1\":\"field 1\"" + it "contains response samples - dt1field2" $ + md `shouldContain` "\"dt1field2\":13" + it "contains request body samples" $ md `shouldContain` "17" From 269e546a6a6b4e293a5ed67250fa2f1154c48274 Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Thu, 18 Mar 2021 08:48:30 +0100 Subject: [PATCH 086/156] sqlite-simple cookbook is working with sqlite-simple >= 0.4.5.0 --- .gitignore | 1 - cabal.project | 5 +---- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 6cec8e9d..a74ddee2 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,6 @@ doc/_build doc/venv doc/tutorial/static/api.js doc/tutorial/static/jq.js -shell.nix # nix result* diff --git a/cabal.project b/cabal.project index b8516e66..337ef6ae 100644 --- a/cabal.project +++ b/cabal.project @@ -26,7 +26,7 @@ packages: 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 @@ -54,9 +54,6 @@ constraints: 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 diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index e8e1588f..108b85c8 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -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 From d6fb3826c847d3be3d748d2f685ef15eb491b4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Thu, 18 Mar 2021 11:41:18 +0100 Subject: [PATCH 087/156] Try and improve caching --- .github/workflows/main.yml | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 852ffdd6..91b4f6aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,20 +32,24 @@ jobs: ghc-version: ${{ matrix.ghc }} cabal-version: ${{ matrix.cabal }} - - name: Configure - run: | - cabal configure --enable-tests --enable-benchmarks --test-show-details=direct - cabal install --ignore-project -j2 doctest --constraint='doctest ^>=0.17' - - 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 + name: Cache ~/.cabal/store and dist-newstyle with: - path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }} + 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.17' - name: Build run: | @@ -115,16 +119,17 @@ jobs: stack test --system-ghc ghcjs: - name: cabal / ghcjs 8.4 + name: ubuntu-18.04 / ghcjs 8.4 runs-on: "ubuntu-18.04" steps: - uses: actions/checkout@v2 + - name: "Setup PATH" run: | - echo "PATH=$HOME/.cabal/bin:$PATH" >> $GITHUB_ENV + echo "PATH=$HOME/.cabal/bin:/opt/ghcjs/8.4/bin:$PATH" >> $GITHUB_ENV - - name: Install tools + - name: Install ghcjs and cabal run: | sudo add-apt-repository ppa:hvr/ghcjs sudo apt-get update -y @@ -135,7 +140,23 @@ jobs: cp cabal.ghcjs.project cabal.project cat cabal.project + - name: Cabal update and freeze + 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: ubuntu-18.04-ghcjs8.4-${{ hashFiles('cabal.project.freeze') }} + restore-keys: | + ubuntu-18.04-ghcjs8.4- + + - name: Install cabal-plan and hspec-discover + run: | cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project hspec-discover From a28856a11a9e127b269dd851db942f6cc326d018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Thu, 18 Mar 2021 12:10:10 +0100 Subject: [PATCH 088/156] Rename GH action file to match branch name --- .github/workflows/{main.yml => master.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{main.yml => master.yml} (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/master.yml similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/master.yml From ba379287c80e502e47664597370cef4b5593e417 Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Tue, 23 Mar 2021 14:09:49 +0100 Subject: [PATCH 089/156] reverting : removing DerivingVia extension (not compatible ghc < 8.6.1) --- doc/cookbook/uverb/UVerb.lhs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/cookbook/uverb/UVerb.lhs b/doc/cookbook/uverb/UVerb.lhs index 2119706c..93147b68 100644 --- a/doc/cookbook/uverb/UVerb.lhs +++ b/doc/cookbook/uverb/UVerb.lhs @@ -10,6 +10,8 @@ handlers that respond with arbitrary open unions of types. {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} @@ -154,12 +156,8 @@ Example usage: ```haskell data Foo = Foo Int Int Int - deriving (Show, Eq, GHC.Generic) - -instance ToJSON Foo - -instance HasStatus Foo where - type StatusOf Foo = 200 + deriving (Show, Eq, GHC.Generic, ToJSON) + deriving HasStatus via WithStatus 200 Foo data Bar = Bar deriving (Show, Eq, GHC.Generic) From d4f7b0397dae2466d1c6d4aeb4579c770dd61bd8 Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Tue, 23 Mar 2021 14:10:14 +0100 Subject: [PATCH 090/156] adding a compatibility warning in the cookbook --- doc/cookbook/uverb/UVerb.lhs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/cookbook/uverb/UVerb.lhs b/doc/cookbook/uverb/UVerb.lhs index 93147b68..52566ca2 100644 --- a/doc/cookbook/uverb/UVerb.lhs +++ b/doc/cookbook/uverb/UVerb.lhs @@ -4,6 +4,10 @@ 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 From c3a517cb4f7efd89a031f134dab43b4f3d85217b Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 3 Feb 2021 07:52:28 +0100 Subject: [PATCH 091/156] doc(servant-foreign): Inflection docs & module docs --- servant-foreign/src/Servant/Foreign.hs | 2 ++ .../src/Servant/Foreign/Inflections.hs | 30 +++++++++++-------- .../src/Servant/Foreign/Internal.hs | 5 ++-- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/servant-foreign/src/Servant/Foreign.hs b/servant-foreign/src/Servant/Foreign.hs index aa1a5573..1df5bf74 100644 --- a/servant-foreign/src/Servant/Foreign.hs +++ b/servant-foreign/src/Servant/Foreign.hs @@ -1,5 +1,7 @@ -- | Generalizes all the data needed to make code generation work with -- arbitrary programming languages. +-- +-- See documentation of 'HasForeignType' for a simple example. 'listFromAPI' returns a list of all your endpoints and their foreign types, given a mapping from Haskell types to foreign types (conventionally called `ftypes` below). module Servant.Foreign ( ArgType(..) , HeaderArg(..) diff --git a/servant-foreign/src/Servant/Foreign/Inflections.hs b/servant-foreign/src/Servant/Foreign/Inflections.hs index 793ea36d..42f89927 100644 --- a/servant-foreign/src/Servant/Foreign/Inflections.hs +++ b/servant-foreign/src/Servant/Foreign/Inflections.hs @@ -20,20 +20,31 @@ import Prelude hiding (head, tail) import Servant.Foreign.Internal +-- | Simply concat each part of the FunctionName together. +-- +-- @[ "get", "documents", "by", "id" ] → "getdocumentsbyid"@ +concatCase :: FunctionName -> Text +concatCase = view concatCaseL + concatCaseL :: Getter FunctionName Text concatCaseL = _FunctionName . to mconcat --- | Function name builder that simply concat each part together -concatCase :: FunctionName -> Text -concatCase = view concatCaseL +-- | Use the snake_case convention. +-- Each part is separated by a single underscore character. +-- +-- @[ "get", "documents", "by", "id" ] → "get_documents_by_id"@ +snakeCase :: FunctionName -> Text +snakeCase = view snakeCaseL snakeCaseL :: Getter FunctionName Text snakeCaseL = _FunctionName . to (intercalate "_") --- | Function name builder using the snake_case convention. --- each part is separated by a single underscore character. -snakeCase :: FunctionName -> Text -snakeCase = view snakeCaseL +-- | Use the camelCase convention. +-- The first part is lower case, every other part starts with an upper case character. +-- +-- @[ "get", "documents", "by", "id" ] → "getDocumentsById"@ +camelCase :: FunctionName -> Text +camelCase = view camelCaseL camelCaseL :: Getter FunctionName Text camelCaseL = _FunctionName . to convert @@ -42,8 +53,3 @@ camelCaseL = _FunctionName . to convert convert (p:ps) = mconcat $ p : map capitalize ps capitalize "" = "" capitalize name = C.toUpper (head name) `cons` tail name - --- | Function name builder using the CamelCase convention. --- each part begins with an upper case character. -camelCase :: FunctionName -> Text -camelCase = view camelCaseL diff --git a/servant-foreign/src/Servant/Foreign/Internal.hs b/servant-foreign/src/Servant/Foreign/Internal.hs index 22f37ad9..5c82c056 100644 --- a/servant-foreign/src/Servant/Foreign/Internal.hs +++ b/servant-foreign/src/Servant/Foreign/Internal.hs @@ -13,8 +13,6 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} --- | Generalizes all the data needed to make code generation work with --- arbitrary programming languages. module Servant.Foreign.Internal where import Prelude () @@ -40,6 +38,9 @@ import Servant.API.Modifiers (RequiredArgument) import Servant.API.TypeLevel +-- | Canonical name of the endpoint, can be used to generate a function name. +-- +-- You can use the functions in "Servant.Foreign.Inflections", like 'Servant.Foreign.Inflections.camelCase' to transform to `Text`. newtype FunctionName = FunctionName { unFunctionName :: [Text] } deriving (Data, Show, Eq, Semigroup, Monoid, Typeable) From a0265097e8f6c37542877bdc110f1845b3bb4798 Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 3 Feb 2021 08:03:33 +0100 Subject: [PATCH 092/156] 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`). --- servant-foreign/src/Servant/Foreign.hs | 29 +++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/servant-foreign/src/Servant/Foreign.hs b/servant-foreign/src/Servant/Foreign.hs index 1df5bf74..e8e47ab6 100644 --- a/servant-foreign/src/Servant/Foreign.hs +++ b/servant-foreign/src/Servant/Foreign.hs @@ -3,20 +3,28 @@ -- -- See documentation of 'HasForeignType' for a simple example. 'listFromAPI' returns a list of all your endpoints and their foreign types, given a mapping from Haskell types to foreign types (conventionally called `ftypes` below). module Servant.Foreign - ( ArgType(..) + ( listFromAPI + , Req(..) + , defReq + , HasForeignType(..) + , GenerateList(..) + , HasForeign(..) + , NoTypes + , ArgType(..) , HeaderArg(..) , QueryArg(..) - , Req(..) , ReqBodyContentType(..) , Segment(..) + , isCapture + , captureArg , SegmentType(..) , Url(..) - -- aliases + -- * aliases , Path , Arg(..) , FunctionName(..) , PathSegment(..) - -- lenses + -- * lenses , argName , argType , argPath @@ -32,7 +40,7 @@ module Servant.Foreign , queryArgName , queryArgType , headerArg - -- prisms + -- * prisms , _PathSegment , _HeaderArg , _ReplaceHeaderArg @@ -41,16 +49,7 @@ module Servant.Foreign , _Normal , _Flag , _List - -- rest of it - , HasForeign(..) - , HasForeignType(..) - , GenerateList(..) - , NoTypes - , captureArg - , isCapture - , defReq - , listFromAPI - -- re-exports + -- * re-exports , module Servant.API , module Servant.Foreign.Inflections ) where From 07f7954cc6c0263cc736a9da58c67216fd83d531 Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 3 Feb 2021 13:00:01 +0100 Subject: [PATCH 093/156] chore(servant-foreign): remove dead type `Frag` It is not used anywhere and also not exported. --- servant-foreign/src/Servant/Foreign/Internal.hs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/servant-foreign/src/Servant/Foreign/Internal.hs b/servant-foreign/src/Servant/Foreign/Internal.hs index 5c82c056..1fabfeed 100644 --- a/servant-foreign/src/Servant/Foreign/Internal.hs +++ b/servant-foreign/src/Servant/Foreign/Internal.hs @@ -85,11 +85,6 @@ captureArg _ = error "captureArg called on non capture" type Path f = [Segment f] -newtype Frag f = Frag { unFragment :: Arg f } - deriving (Data, Eq, Show, Typeable) - -makePrisms ''Frag - data ArgType = Normal | Flag From e4865644c13e451d4a26a497641e3cc0b4c35050 Mon Sep 17 00:00:00 2001 From: Philip Patsch Date: Wed, 3 Feb 2021 17:12:57 +0100 Subject: [PATCH 094/156] doc(servant-foreign): Document module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- servant-foreign/src/Servant/Foreign.hs | 26 ++-- .../src/Servant/Foreign/Internal.hs | 146 ++++++++++++++---- 2 files changed, 133 insertions(+), 39 deletions(-) diff --git a/servant-foreign/src/Servant/Foreign.hs b/servant-foreign/src/Servant/Foreign.hs index e8e47ab6..6c50889e 100644 --- a/servant-foreign/src/Servant/Foreign.hs +++ b/servant-foreign/src/Servant/Foreign.hs @@ -3,28 +3,30 @@ -- -- See documentation of 'HasForeignType' for a simple example. 'listFromAPI' returns a list of all your endpoints and their foreign types, given a mapping from Haskell types to foreign types (conventionally called `ftypes` below). module Servant.Foreign - ( listFromAPI + ( + -- * Main API + listFromAPI , Req(..) , defReq , HasForeignType(..) , GenerateList(..) , HasForeign(..) , NoTypes - , ArgType(..) - , HeaderArg(..) - , QueryArg(..) - , ReqBodyContentType(..) + -- * Subtypes of 'Req' + , Url(..) + , Path , Segment(..) + , SegmentType(..) , isCapture , captureArg - , SegmentType(..) - , Url(..) - -- * aliases - , Path + , QueryArg(..) + , ArgType(..) + , HeaderArg(..) , Arg(..) , FunctionName(..) + , ReqBodyContentType(..) , PathSegment(..) - -- * lenses + -- * Lenses , argName , argType , argPath @@ -40,7 +42,7 @@ module Servant.Foreign , queryArgName , queryArgType , headerArg - -- * prisms + -- * Prisms , _PathSegment , _HeaderArg , _ReplaceHeaderArg @@ -49,7 +51,7 @@ module Servant.Foreign , _Normal , _Flag , _List - -- * re-exports + -- * Re-exports , module Servant.API , module Servant.Foreign.Inflections ) where diff --git a/servant-foreign/src/Servant/Foreign/Internal.hs b/servant-foreign/src/Servant/Foreign/Internal.hs index 1fabfeed..356f9681 100644 --- a/servant-foreign/src/Servant/Foreign/Internal.hs +++ b/servant-foreign/src/Servant/Foreign/Internal.hs @@ -46,45 +46,67 @@ newtype FunctionName = FunctionName { unFunctionName :: [Text] } makePrisms ''FunctionName +-- | See documentation of 'Arg' newtype PathSegment = PathSegment { unPathSegment :: Text } deriving (Data, Show, Eq, IsString, Semigroup, Monoid, Typeable) makePrisms ''PathSegment -data Arg f = Arg +-- | Maps a name to the foreign type that belongs to the annotated value. +-- +-- Used for header args, query args, and capture args. +data Arg ftype = Arg { _argName :: PathSegment - , _argType :: f } + -- ^ The name to be captured. + -- + -- Only for capture args it really denotes a path segment. + , _argType :: ftype + -- ^ Foreign type the associated value will have + } deriving (Data, Eq, Show, Typeable) makeLenses ''Arg -argPath :: Getter (Arg f) Text +argPath :: Getter (Arg ftype) Text argPath = argName . _PathSegment -data SegmentType f +data SegmentType ftype = Static PathSegment - -- ^ a static path segment. like "/foo" - | Cap (Arg f) - -- ^ a capture. like "/:userid" + -- ^ Static path segment. + -- + -- @"foo\/bar\/baz"@ + -- + -- contains the static segments @"foo"@, @"bar"@ and @"baz"@. + | Cap (Arg ftype) + -- ^ A capture. + -- + -- @"user\/{userid}\/name"@ + -- + -- would capture the arg @userid@ with type @ftype@. deriving (Data, Eq, Show, Typeable) makePrisms ''SegmentType -newtype Segment f = Segment { unSegment :: SegmentType f } +-- | A part of the Url’s path. +newtype Segment ftype = Segment { unSegment :: SegmentType ftype } deriving (Data, Eq, Show, Typeable) makePrisms ''Segment -isCapture :: Segment f -> Bool +-- | Whether a segment is a 'Cap'. +isCapture :: Segment ftype -> Bool isCapture (Segment (Cap _)) = True isCapture _ = False -captureArg :: Segment f -> Arg f +-- | Crashing Arg extraction from segment, TODO: remove +captureArg :: Segment ftype -> Arg ftype captureArg (Segment (Cap s)) = s captureArg _ = error "captureArg called on non capture" -type Path f = [Segment f] +-- TODO: remove, unnecessary indirection +type Path ftype = [Segment ftype] +-- | Type of a 'QueryArg'. data ArgType = Normal | Flag @@ -93,18 +115,41 @@ data ArgType makePrisms ''ArgType -data QueryArg f = QueryArg - { _queryArgName :: Arg f +-- | Url Query argument. +-- +-- Urls can contain query arguments, which is a list of key-value pairs. +-- In a typical url, query arguments look like this: +-- +-- @?foo=bar&alist[]=el1&alist[]=el2&aflag@ +-- +-- Each pair can be +-- +-- * @?foo=bar@: a plain key-val pair, either optional or required ('QueryParam') +-- * @?aflag@: a flag (no value, implicitly Bool with default `false` if it’s missing) ('QueryFlag') +-- * @?alist[]=el1&alist[]=el2@: list of values ('QueryParams') +-- +-- @_queryArgType@ will be set accordingly. +-- +-- For the plain key-val pairs ('QueryParam'), @_queryArgName@’s @ftype@ will be wrapped in a @Maybe@ if the argument is optional. +data QueryArg ftype = QueryArg + { _queryArgName :: Arg ftype + -- ^ Name and foreign type of the argument. Will be wrapped in `Maybe` if the query is optional and in a `[]` if the query is a list , _queryArgType :: ArgType + -- ^ one of normal/plain, list or flag } deriving (Data, Eq, Show, Typeable) makeLenses ''QueryArg -data HeaderArg f = HeaderArg - { _headerArg :: Arg f } +data HeaderArg ftype = + -- | The name of the header and the foreign type of its value. + HeaderArg + { _headerArg :: Arg ftype } + -- | Unused, will never be set. + -- + -- TODO: remove | ReplaceHeaderArg - { _headerArg :: Arg f + { _headerArg :: Arg ftype , _headerPattern :: Text } deriving (Data, Eq, Show, Typeable) @@ -113,29 +158,71 @@ makeLenses ''HeaderArg makePrisms ''HeaderArg -data Url f = Url - { _path :: Path f - , _queryStr :: [QueryArg f] - , _frag :: Maybe f +-- | Full endpoint url, with all captures and parameters +data Url ftype = Url + { _path :: Path ftype + -- ^ Url path, list of either static segments or captures + -- + -- @"foo\/{id}\/bar"@ + , _queryStr :: [QueryArg ftype] + -- ^ List of query args + -- + -- @"?foo=bar&a=b"@ + , _frag :: Maybe ftype + -- ^ Url fragment. + -- + -- Not sent to the HTTP server, so only useful for frontend matters (e.g. inter-page linking). + -- + -- @#fragmentText@ } deriving (Data, Eq, Show, Typeable) -defUrl :: Url f +defUrl :: Url ftype defUrl = Url [] [] Nothing makeLenses ''Url +-- | See documentation of '_reqBodyContentType' data ReqBodyContentType = ReqBodyJSON | ReqBodyMultipart deriving (Data, Eq, Show, Read) -data Req f = Req - { _reqUrl :: Url f +-- | Full description of an endpoint in your API, generated by 'listFromAPI'. It should give you all the information needed to generate foreign language bindings. +-- +-- Every field containing @ftype@ will use the foreign type mapping specified via 'HasForeignType' (see its docstring on how to set that up). +-- +-- See https://docs.servant.dev/en/stable/tutorial/ApiType.html for accessible documentation of the possible content of an endpoint. +data Req ftype = Req + { _reqUrl :: Url ftype + -- ^ Full list of URL segments, including captures , _reqMethod :: HTTP.Method - , _reqHeaders :: [HeaderArg f] - , _reqBody :: Maybe f - , _reqReturnType :: Maybe f + -- ^ @\"GET\"@\/@\"POST\"@\/@\"PUT\"@\/… + , _reqHeaders :: [HeaderArg ftype] + -- ^ Headers required by this endpoint, with their type + , _reqBody :: Maybe ftype + -- ^ Foreign type of the expected request body ('ReqBody'), if any + , _reqReturnType :: Maybe ftype + -- ^ The foreign type of the response, if any , _reqFuncName :: FunctionName + -- ^ The URL segments rendered in a way that they can be easily concatenated into a canonical function name , _reqBodyContentType :: ReqBodyContentType + -- ^ The content type the request body is transferred as. + -- + -- This is a severe limitation of @servant-foreign@ currently, + -- as we only allow the content type to be `JSON` + -- no user-defined content types. ('ReqBodyMultipart' is not + -- actually implemented.) + -- + -- Thus, any routes looking like this will work: + -- + -- @"foo" :> Get '[JSON] Foo@ + -- + -- while routes like + -- + -- @"foo" :> Get '[MyFancyContentType] Foo@ + -- + -- will fail with an error like + -- + -- @• JSON expected in list '[MyFancyContentType]@ } deriving (Data, Eq, Show, Typeable) @@ -179,11 +266,16 @@ defReq = Req defUrl "GET" [] Nothing Nothing (FunctionName []) ReqBodyJSON class HasForeignType lang ftype a where typeFor :: Proxy lang -> Proxy ftype -> Proxy a -> ftype +-- | The language definition without any foreign types. It can be used for dynamic languages which do not /do/ type annotations. data NoTypes -instance HasForeignType NoTypes NoContent ftype where +-- | Use if the foreign language does not have any types. +instance HasForeignType NoTypes NoContent a where typeFor _ _ _ = NoContent +-- | Implementation of the Servant framework types. +-- +-- Relevant instances: Everything containing 'HasForeignType'. class HasForeign lang ftype (api :: *) where type Foreign ftype api :: * foreignFor :: Proxy lang -> Proxy ftype -> Proxy api -> Req ftype -> Foreign ftype api From 81a73dfcdac23066d457c93fbfb0c788e2b7a344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Fri, 9 Apr 2021 15:56:59 +0200 Subject: [PATCH 095/156] Try excluding uverb cookbook from pre 8.6.1 builds --- doc/cookbook/uverb/uverb.cabal | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal index 8b6b0b45..6c984070 100644 --- a/doc/cookbook/uverb/uverb.cabal +++ b/doc/cookbook/uverb/uverb.cabal @@ -28,6 +28,8 @@ executable cookbook-uverb , swagger2 , wai , warp - default-language: Haskell2010 + if impl(ghc < 8.6.1) + buildable: False + default-language: Haskell2010 ghc-options: -Wall -pgmL markdown-unlit build-tool-depends: markdown-unlit:markdown-unlit From f30b72cc902d17d8c6e17799d204f71e18bc8505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Fri, 9 Apr 2021 16:19:51 +0200 Subject: [PATCH 096/156] Add DeriveAnyClass to UVerb.lhs (not implied by DerivingVia on 8.6.5) --- doc/cookbook/uverb/UVerb.lhs | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/cookbook/uverb/UVerb.lhs b/doc/cookbook/uverb/UVerb.lhs index 52566ca2..cc771111 100644 --- a/doc/cookbook/uverb/UVerb.lhs +++ b/doc/cookbook/uverb/UVerb.lhs @@ -15,6 +15,7 @@ handlers that respond with arbitrary open unions of types. {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} From 4a7a1080a0dc385ec8c9cf0e5f0beac0aeb357bf Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Fri, 9 Apr 2021 17:09:03 +0200 Subject: [PATCH 097/156] pinning nixpkgs and adding missing dependencies --- README.md | 6 ++++++ nix/README.md | 6 ++++++ nix/shell.nix | 40 ++++++++++++++++++++++------------------ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0d06a4e8..9ffb6311 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,9 @@ To compare with `reitit` (Clojure framework) ``` 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) diff --git a/nix/README.md b/nix/README.md index 56400fbd..4dfc519d 100644 --- a/nix/README.md +++ b/nix/README.md @@ -21,3 +21,9 @@ a particular ghc version, e.g: ``` sh $ nix-shell nix/shell.nix --argstr compiler ghcHEAD ``` + +**Possible GHC versions** +- `ghc822Binary` +- `ghc865` +- `ghc884` +- `ghc8102` - default diff --git a/nix/shell.nix b/nix/shell.nix index 4e43c606..5fa96923 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,21 +1,25 @@ -{ pkgs ? import {} -, compiler ? "ghc822" +let nixos = fetchTarball { + url = "https://releases.nixos.org/nixos/20.09/nixos-20.09.3505.12d9950bf47/nixexprs.tar.xz"; + sha256 = "0fsl8bsdb8i536pfs4wrp0826h5l84xqlwx32sbz66jg4ykqp9lr"; +}; in + +{ compiler ? "ghc8102" , tutorial ? false +, pkgs ? import nixos { config = {}; } }: + + with pkgs; -with pkgs; - -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 ] - ++ (if tutorial then [docstuffs postgresql] else []); - shellHook = '' - eval $(grep export ${ghc}/bin/ghc) - export LD_LIBRARY_PATH="${zlib}/lib"; - ''; -} + 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 cabal-install postgresql openssl ] + ++ (if tutorial then [docstuffs postgresql] else []); + shellHook = '' + eval $(grep export ${ghc}/bin/ghc) + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"${zlib}/lib"; + ''; + } From bbd016df090969242d6ee888fd57c1cbb5d1291d Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Sat, 10 Apr 2021 12:30:31 +0200 Subject: [PATCH 098/156] enabling https cookbook --- cabal.project | 2 +- doc/cookbook/https/https.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 337ef6ae..4d19f787 100644 --- a/cabal.project +++ b/cabal.project @@ -30,7 +30,7 @@ packages: doc/cookbook/file-upload doc/cookbook/generic -- doc/cookbook/hoist-server-with-context - -- doc/cookbook/https + doc/cookbook/https -- doc/cookbook/jwt-and-basic-auth/ doc/cookbook/pagination -- doc/cookbook/sentry diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index ec778b1b..e2ca9c15 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -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 From 97967d87d1af60bb641572c5565c381a4c04fff9 Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Fri, 16 Apr 2021 13:39:09 +0200 Subject: [PATCH 099/156] enabling hoist-server-with-context cookbook --- cabal.project | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 4d19f787..da0f60ad 100644 --- a/cabal.project +++ b/cabal.project @@ -29,7 +29,7 @@ packages: doc/cookbook/db-sqlite-simple doc/cookbook/file-upload doc/cookbook/generic - -- doc/cookbook/hoist-server-with-context + doc/cookbook/hoist-server-with-context doc/cookbook/https -- doc/cookbook/jwt-and-basic-auth/ doc/cookbook/pagination diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index 06bdc5c8..7db1ab7e 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -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 @@ -32,6 +32,8 @@ executable cookbook-hoist-server-with-context , http-types >= 0.12 , bytestring >= 0.10.4 , mtl + if impl(ghc < 8.2.1) + buildable: False default-language: Haskell2010 ghc-options: -Wall -pgmL markdown-unlit build-tool-depends: markdown-unlit:markdown-unlit From 3c520683ce1349d5e5c94d72ef22a79ce3019d69 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Wed, 21 Apr 2021 06:16:17 +0800 Subject: [PATCH 100/156] Allow singleton-bool 0.1.6 Builds fine and all tests pass. --- servant/servant.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant/servant.cabal b/servant/servant.cabal index 260e9113..d5d8d62f 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -90,7 +90,7 @@ library -- i.e. re-export, or allow using without direct dependency build-depends: http-api-data >= 0.4.1 && < 0.4.3 - , singleton-bool >= 0.1.4 && < 0.1.6 + , singleton-bool >= 0.1.4 && < 0.1.7 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. From 448c444db665730d40df46f86f04d1b04dcfb32f Mon Sep 17 00:00:00 2001 From: fisx Date: Fri, 23 Apr 2021 10:37:48 +0200 Subject: [PATCH 101/156] Typo (#1416) --- servant/src/Servant/API/UVerb/Union.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant/src/Servant/API/UVerb/Union.hs b/servant/src/Servant/API/UVerb/Union.hs index 1b776216..774cf628 100644 --- a/servant/src/Servant/API/UVerb/Union.hs +++ b/servant/src/Servant/API/UVerb/Union.hs @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -} -- | Type-level code for implementing and using 'UVerb'. Heavily inspired by --- [world-piece](https://github.com/cdepillabout/world-peace). +-- [world-peace](https://github.com/cdepillabout/world-peace). module Servant.API.UVerb.Union ( IsMember , Unique From 507f0a4671e05027a0521066bebc70f8755da97e Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 14 May 2021 12:34:04 +0300 Subject: [PATCH 102/156] Allow hspec < 2.9 https://github.com/commercialhaskell/stackage/issues/6010 --- servant-client-core/servant-client-core.cabal | 4 ++-- servant-client/servant-client.cabal | 4 ++-- servant-foreign/servant-foreign.cabal | 5 ++--- servant-http-streams/servant-http-streams.cabal | 4 ++-- servant-server/servant-server.cabal | 4 ++-- servant/servant.cabal | 4 ++-- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 9c99f10c..61de0ce8 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -103,8 +103,8 @@ test-suite spec -- Additional dependencies build-depends: deepseq >= 1.4.2.0 && < 1.5 - , hspec >= 2.6.0 && < 2.8 + , hspec >= 2.6.0 && < 2.9 , QuickCheck >= 2.12.6.1 && < 2.15 build-tool-depends: - hspec-discover:hspec-discover >= 2.6.0 && <2.8 + hspec-discover:hspec-discover >= 2.6.0 && <2.9 diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 9f6a6f64..7c350257 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -119,7 +119,7 @@ test-suite spec -- Additional dependencies build-depends: entropy >= 0.4.1.3 && < 0.5 - , hspec >= 2.6.0 && < 2.8 + , hspec >= 2.6.0 && < 2.9 , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 , QuickCheck >= 2.12.6.1 && < 2.15 @@ -128,7 +128,7 @@ test-suite spec , tdigest >= 0.2 && < 0.3 build-tool-depends: - hspec-discover:hspec-discover >= 2.6.0 && < 2.8 + hspec-discover:hspec-discover >= 2.6.0 && < 2.9 test-suite readme type: exitcode-stdio-1.0 diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 5a821369..4ce32d2d 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -74,8 +74,7 @@ test-suite spec -- Additional dependencies build-depends: - hspec >= 2.6.0 && <2.8 - + hspec >= 2.6.0 && <2.9 build-tool-depends: - hspec-discover:hspec-discover >=2.6.0 && <2.8 + hspec-discover:hspec-discover >=2.6.0 && <2.9 default-language: Haskell2010 diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 055dc3d5..ecdee5c6 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -112,7 +112,7 @@ test-suite spec -- Additional dependencies build-depends: entropy >= 0.4.1.3 && < 0.5 - , hspec >= 2.6.0 && < 2.8 + , hspec >= 2.6.0 && < 2.9 , HUnit >= 1.6.0.0 && < 1.7 , network >= 2.8.0.0 && < 3.2 , QuickCheck >= 2.12.6.1 && < 2.15 @@ -121,7 +121,7 @@ test-suite spec , tdigest >= 0.2 && < 0.3 build-tool-depends: - hspec-discover:hspec-discover >= 2.6.0 && < 2.8 + hspec-discover:hspec-discover >= 2.6.0 && < 2.9 test-suite readme type: exitcode-stdio-1.0 diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index f4833943..98c65d26 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -157,7 +157,7 @@ test-suite spec build-depends: aeson >= 1.4.1.0 && < 1.6 , directory >= 1.3.0.0 && < 1.4 - , hspec >= 2.6.0 && < 2.8 + , hspec >= 2.6.0 && < 2.9 , hspec-wai >= 0.10.1 && < 0.12 , QuickCheck >= 2.12.6.1 && < 2.15 , should-not-typecheck >= 2.1.0 && < 2.2 @@ -165,4 +165,4 @@ test-suite spec , wai-extra >= 3.0.24.3 && < 3.2 build-tool-depends: - hspec-discover:hspec-discover >= 2.6.0 && <2.8 + hspec-discover:hspec-discover >= 2.6.0 && <2.9 diff --git a/servant/servant.cabal b/servant/servant.cabal index d5d8d62f..543887a3 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -163,9 +163,9 @@ test-suite spec -- Additional dependencies build-depends: - hspec >= 2.6.0 && < 2.8 + hspec >= 2.6.0 && < 2.9 , QuickCheck >= 2.12.6.1 && < 2.15 , quickcheck-instances >= 0.3.19 && < 0.4 build-tool-depends: - hspec-discover:hspec-discover >= 2.6.0 && < 2.8 + hspec-discover:hspec-discover >= 2.6.0 && < 2.9 From 4016aafe6683eaa1e0fba303fb015b5c1a1ec624 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Fri, 14 May 2021 16:34:16 +0300 Subject: [PATCH 103/156] CI: add ppa:hvr/ghc in ghcjs build (#1421) --- .github/workflows/master.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 91b4f6aa..6a50181c 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -131,6 +131,8 @@ jobs: - name: Install ghcjs and cabal run: | + # Default GitHub image dropped ppa:hvr/ghc, so we add it ourselves + sudo add-apt-repository ppa:hvr/ghc sudo add-apt-repository ppa:hvr/ghcjs sudo apt-get update -y sudo apt-get install ghcjs-8.4 From 26f0f938742978c6313f8a40c384941229eaad0f Mon Sep 17 00:00:00 2001 From: Nathan van Doorn Date: Wed, 26 May 2021 09:05:50 +0100 Subject: [PATCH 104/156] Update IRC link in readme to point at libera --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ffb6311..e4a0316b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ 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) or [mailing +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 From da8e64b53402ec7e876afcacf247e9910dc3abe8 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sun, 6 Jun 2021 00:37:35 +0300 Subject: [PATCH 105/156] Allow lens-5.0 (#1426) --- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index d2c8623e..87699f37 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -59,7 +59,7 @@ library , hashable >= 1.2.7.0 && < 1.4 , http-media >= 0.7.1.3 && < 0.9 , http-types >= 0.12.2 && < 0.13 - , lens >= 4.17 && < 4.20 + , lens >= 4.17 && < 5.1 , string-conversions >= 0.4.0.1 && < 0.5 , universe-base >= 1.1.1 && < 1.2 , unordered-containers >= 0.2.9.0 && < 0.3 diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 4ce32d2d..53c3fb2e 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -52,7 +52,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.12 - , lens >= 4.17 && < 4.20 + , lens >= 4.17 && < 5.1 , http-types >= 0.12.2 && < 0.13 hs-source-dirs: src From 0cb2d603c4cb1b1fc80af60b0aeb6e07c699a1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20G=C3=A9lineau?= Date: Tue, 8 Jun 2021 14:28:19 -0400 Subject: [PATCH 106/156] 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. --- servant-docs/golden/comprehensive.md | 4 ++-- servant-docs/src/Servant/Docs/Internal.hs | 24 +++++++++++++++++++- servant/src/Servant/Test/ComprehensiveAPI.hs | 2 +- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/servant-docs/golden/comprehensive.md b/servant-docs/golden/comprehensive.md index 7d224502..9584d499 100644 --- a/servant-docs/golden/comprehensive.md +++ b/servant-docs/golden/comprehensive.md @@ -52,11 +52,11 @@ ``` -## GET /capture/:foo +## GET /capture/:bar ### Captures: -- *foo*: Capture foo Int +- *bar*: example description ### Response: diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index b36ad88b..d19636bf 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -862,7 +862,7 @@ instance HasDocs EmptyAPI where -- | @"books" :> 'Capture' "isbn" Text@ will appear as -- @/books/:isbn@ in the docs. instance (KnownSymbol sym, ToCapture (Capture sym a), HasDocs api) - => HasDocs (Capture' mods sym a :> api) where + => HasDocs (Capture' '[] sym a :> api) where docsFor Proxy (endpoint, action) = docsFor subApiP (endpoint', action') @@ -874,6 +874,28 @@ instance (KnownSymbol sym, ToCapture (Capture sym a), HasDocs api) endpoint' = over path (\p -> p ++ [":" ++ symbolVal symP]) endpoint symP = Proxy :: Proxy sym +instance (KnownSymbol descr, KnownSymbol sym, HasDocs api) + => HasDocs (Capture' (Description descr ': mods) sym a :> api) where + + docsFor Proxy (endpoint, action) = + docsFor subApiP (endpoint', action') + + where subApiP = Proxy :: Proxy api + + docCapture = DocCapture (symbolVal symP) (symbolVal descrP) + action' = over captures (|> docCapture) action + endpoint' = over path (\p -> p ++ [":" ++ symbolVal symP]) endpoint + descrP = Proxy :: Proxy descr + symP = Proxy :: Proxy sym + +instance {-# OVERLAPPABLE #-} HasDocs (Capture' mods sym a :> api) + => HasDocs (Capture' (mod ': mods) sym a :> api) where + + docsFor Proxy = + docsFor apiP + + where apiP = Proxy :: Proxy (Capture' mods sym a :> api) + -- | @"books" :> 'CaptureAll' "isbn" Text@ will appear as -- @/books/:isbn@ in the docs. diff --git a/servant/src/Servant/Test/ComprehensiveAPI.hs b/servant/src/Servant/Test/ComprehensiveAPI.hs index 2c1b02a3..67417869 100644 --- a/servant/src/Servant/Test/ComprehensiveAPI.hs +++ b/servant/src/Servant/Test/ComprehensiveAPI.hs @@ -48,7 +48,7 @@ comprehensiveAPIWithoutStreaming = Proxy type ComprehensiveAPIWithoutStreamingOrRaw' endpoint = GET :<|> "get-int" :> Get '[JSON] Int - :<|> "capture" :> Capture' '[Description "example description"] "foo" Int :> GET + :<|> "capture" :> Capture' '[Description "example description"] "bar" Int :> GET :<|> "capture-lenient" :> Capture' '[Lenient] "foo" Int :> GET :<|> "header" :> Header "foo" Int :> GET :<|> "header-lenient" :> Header' '[Required, Lenient] "bar" Int :> GET From 0f9cc7eeec52f65b90a4bd55811f114ab368b90d Mon Sep 17 00:00:00 2001 From: Paolo Capriotti Date: Thu, 10 Jun 2021 17:10:50 +0200 Subject: [PATCH 107/156] 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. --- .../src/Servant/Client/Core/HasClient.hs | 35 ++++++++--- .../test/Servant/ClientTestUtils.hs | 5 ++ servant-client/test/Servant/SuccessSpec.hs | 9 +++ servant-server/servant-server.cabal | 1 + servant-server/src/Servant/Server/UVerb.hs | 58 +++++++++++++------ servant-server/test/Servant/ServerSpec.hs | 30 +++++++++- servant/src/Servant/API/ResponseHeaders.hs | 3 + 7 files changed, 114 insertions(+), 27 deletions(-) diff --git a/servant-client-core/src/Servant/Client/Core/HasClient.hs b/servant-client-core/src/Servant/Client/Core/HasClient.hs index a030a242..5f7ad3b3 100644 --- a/servant-client-core/src/Servant/Client/Core/HasClient.hs +++ b/servant-client-core/src/Servant/Client/Core/HasClient.hs @@ -75,7 +75,7 @@ import Servant.API NoContentVerb, QueryFlag, QueryParam', QueryParams, Raw, ReflectMethod (..), RemoteHost, ReqBody', SBoolI, Stream, StreamBody', Summary, ToHttpApiData, ToSourceIO (..), Vault, - Verb, WithNamedContext, contentType, getHeadersHList, + Verb, WithNamedContext, WithStatus (..), contentType, getHeadersHList, getResponse, toQueryParam, toUrlPiece) import Servant.API.ContentTypes (contentTypes, AllMime (allMime), AllMimeUnrender (allMimeUnrender)) @@ -318,6 +318,25 @@ instance {-# OVERLAPPING #-} data ClientParseError = ClientParseError MediaType String | ClientStatusMismatch | ClientNoMatchingStatus deriving (Eq, Show) +class UnrenderResponse (cts :: [*]) (a :: *) where + unrenderResponse :: Seq.Seq H.Header -> BL.ByteString -> Proxy cts + -> [Either (MediaType, String) a] + +instance {-# OVERLAPPABLE #-} AllMimeUnrender cts a => UnrenderResponse cts a where + unrenderResponse _ body = map parse . allMimeUnrender + where parse (mediaType, parser) = left ((,) mediaType) (parser body) + +instance {-# OVERLAPPING #-} forall cts a h . (UnrenderResponse cts a, BuildHeadersTo h) + => UnrenderResponse cts (Headers h a) where + unrenderResponse hs body = (map . fmap) setHeaders . unrenderResponse hs body + where + setHeaders :: a -> Headers h a + setHeaders x = Headers x (buildHeadersTo (toList hs)) + +instance {-# OVERLAPPING #-} UnrenderResponse cts a + => UnrenderResponse cts (WithStatus n a) where + unrenderResponse hs body = (map . fmap) WithStatus . unrenderResponse hs body + instance {-# OVERLAPPING #-} ( RunClient m, contentTypes ~ (contentType ': otherContentTypes), @@ -326,7 +345,7 @@ instance {-# OVERLAPPING #-} as ~ (a ': as'), AllMime contentTypes, ReflectMethod method, - All (AllMimeUnrender contentTypes) as, + All (UnrenderResponse contentTypes) as, All HasStatus as, HasStatuses as', Unique (Statuses as) ) => @@ -349,7 +368,8 @@ instance {-# OVERLAPPING #-} let status = responseStatusCode response body = responseBody response - res = tryParsers status $ mimeUnrenders (Proxy @contentTypes) body + headers = responseHeaders response + res = tryParsers status $ mimeUnrenders (Proxy @contentTypes) headers body case res of Left errors -> throwClientError $ DecodeFailure (T.pack (show errors)) response Right x -> return x @@ -370,13 +390,14 @@ instance {-# OVERLAPPING #-} -- | Given a list of types, parses the given response body as each type mimeUnrenders :: forall cts xs. - All (AllMimeUnrender cts) xs => + All (UnrenderResponse cts) xs => Proxy cts -> + Seq.Seq H.Header -> BL.ByteString -> NP ([] :.: Either (MediaType, String)) xs - mimeUnrenders ctp body = cpure_NP - (Proxy @(AllMimeUnrender cts)) - (Comp . map (\(mediaType, parser) -> left ((,) mediaType) (parser body)) . allMimeUnrender $ ctp) + mimeUnrenders ctp headers body = cpure_NP + (Proxy @(UnrenderResponse cts)) + (Comp . unrenderResponse headers body $ ctp) hoistClientMonad _ _ nt s = nt s diff --git a/servant-client/test/Servant/ClientTestUtils.hs b/servant-client/test/Servant/ClientTestUtils.hs index 4b70a7a9..842712e1 100644 --- a/servant-client/test/Servant/ClientTestUtils.hs +++ b/servant-client/test/Servant/ClientTestUtils.hs @@ -32,6 +32,7 @@ import Data.Char (chr, isPrint) import Data.Monoid () import Data.Proxy +import Data.SOP import Data.Text (Text) import qualified Data.Text as Text @@ -121,6 +122,7 @@ type Api = ReqBody '[JSON] [(String, [Rational])] :> Get '[JSON] (String, Maybe Int, Bool, [(String, [Rational])]) :<|> "headers" :> Get '[JSON] (Headers TestHeaders Bool) + :<|> "uverb-headers" :> UVerb 'GET '[JSON] '[ WithStatus 200 (Headers TestHeaders Bool), WithStatus 204 String ] :<|> "deleteContentType" :> DeleteNoContent :<|> "redirectWithCookie" :> Raw :<|> "empty" :> EmptyAPI @@ -150,6 +152,7 @@ getRawFailure :: HTTP.Method -> ClientM Response getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] -> ClientM (String, Maybe Int, Bool, [(String, [Rational])]) getRespHeaders :: ClientM (Headers TestHeaders Bool) +getUVerbRespHeaders :: ClientM (Union '[ WithStatus 200 (Headers TestHeaders Bool), WithStatus 204 String ]) getDeleteContentType :: ClientM NoContent getRedirectWithCookie :: HTTP.Method -> ClientM Response uverbGetSuccessOrRedirect :: Bool @@ -172,6 +175,7 @@ getRoot :<|> getRawFailure :<|> getMultiple :<|> getRespHeaders + :<|> getUVerbRespHeaders :<|> getDeleteContentType :<|> getRedirectWithCookie :<|> EmptyClient @@ -198,6 +202,7 @@ server = serve api ( :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.badRequest400 [] "rawFailure") :<|> (\ a b c d -> return (a, b, c, d)) :<|> (return $ addHeader 1729 $ addHeader "eg2" True) + :<|> (pure . Z . I . WithStatus $ addHeader 1729 $ addHeader "eg2" True) :<|> return NoContent :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.found302 [("Location", "testlocation"), ("Set-Cookie", "testcookie=test")] "") :<|> emptyServer diff --git a/servant-client/test/Servant/SuccessSpec.hs b/servant-client/test/Servant/SuccessSpec.hs index bb9d47dc..b8e94f01 100644 --- a/servant-client/test/Servant/SuccessSpec.hs +++ b/servant-client/test/Servant/SuccessSpec.hs @@ -32,6 +32,7 @@ import Data.Foldable import Data.Maybe (listToMaybe) import Data.Monoid () +import Data.SOP (NS (..), I (..)) import Data.Text (Text) import qualified Network.HTTP.Client as C @@ -129,6 +130,14 @@ successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do Left e -> assertFailure $ show e Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")] + it "Returns headers on UVerb requests" $ \(_, baseUrl) -> do + res <- runClient getUVerbRespHeaders baseUrl + case res of + Left e -> assertFailure $ show e + Right (Z (I (WithStatus val))) -> + getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")] + Right (S _) -> assertFailure "expected first alternative of union" + it "Stores Cookie in CookieJar after a redirect" $ \(_, baseUrl) -> do mgr <- C.newManager C.defaultManagerSettings cj <- atomically . newTVar $ C.createCookieJar [] diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 98c65d26..a2a95540 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -147,6 +147,7 @@ test-suite spec , safe , servant , servant-server + , sop-core , string-conversions , text , transformers diff --git a/servant-server/src/Servant/Server/UVerb.hs b/servant-server/src/Servant/Server/UVerb.hs index f4096f5b..4b934d91 100644 --- a/servant-server/src/Servant/Server/UVerb.hs +++ b/servant-server/src/Servant/Server/UVerb.hs @@ -22,15 +22,17 @@ module Servant.Server.UVerb ) where +import qualified Data.ByteString as B import Data.Proxy (Proxy (Proxy)) import Data.SOP (I (I)) import Data.SOP.Constraint (All, And) import Data.String.Conversions (LBS, cs) -import Network.HTTP.Types (Status, hContentType) -import Network.Wai (responseLBS) +import Network.HTTP.Types (Status, HeaderName, hContentType) +import Network.Wai (responseLBS, Request) import Servant.API (ReflectMethod, reflectMethod) import Servant.API.ContentTypes (AllCTRender (handleAcceptH), AllMime) -import Servant.API.UVerb (HasStatus, IsMember, Statuses, UVerb, Union, Unique, foldMapUnion, inject, statusOf) +import Servant.API.ResponseHeaders (GetHeaders (..), Headers (..)) +import Servant.API.UVerb (HasStatus, IsMember, Statuses, UVerb, Union, Unique, WithStatus (..), foldMapUnion, inject, statusOf) import Servant.Server.Internal (Context, Delayed, Handler, HasServer (..), RouteResult (FailFatal, Route), Router, Server, ServerT, acceptCheck, addAcceptCheck, addMethodCheck, allowedMethodHead, err406, getAcceptHeader, leafRouter, methodCheck, runAction) @@ -43,13 +45,38 @@ respond :: f (Union xs) respond = pure . inject . I --- | Helper constraint used in @instance 'HasServer' 'UVerb'@. -type IsServerResource contentTypes = AllCTRender contentTypes `And` HasStatus +class IsServerResource (cts :: [*]) a where + resourceResponse :: Request -> Proxy cts -> a -> Maybe (LBS, LBS) + resourceHeaders :: Proxy cts -> a -> [(HeaderName, B.ByteString)] + +instance {-# OVERLAPPABLE #-} AllCTRender cts a + => IsServerResource cts a where + resourceResponse request p res = handleAcceptH p (getAcceptHeader request) res + resourceHeaders _ _ = [] + +instance {-# OVERLAPPING #-} (IsServerResource cts a, GetHeaders (Headers h a)) + => IsServerResource cts (Headers h a) where + resourceResponse request p res = resourceResponse request p (getResponse res) + resourceHeaders cts res = getHeaders res ++ resourceHeaders cts (getResponse res) + +instance {-# OVERLAPPING #-} IsServerResource cts a + => IsServerResource cts (WithStatus n a) where + resourceResponse request p (WithStatus x) = resourceResponse request p x + resourceHeaders cts (WithStatus x) = resourceHeaders cts x + +encodeResource :: forall a cts . (IsServerResource cts a, HasStatus a) + => Request -> Proxy cts -> a + -> (Status, Maybe (LBS, LBS), [(HeaderName, B.ByteString)]) +encodeResource request cts res = (statusOf (Proxy @a), + resourceResponse request cts res, + resourceHeaders cts res) + +type IsServerResourceWithStatus cts = IsServerResource cts `And` HasStatus instance ( ReflectMethod method, AllMime contentTypes, - All (IsServerResource contentTypes) as, + All (IsServerResourceWithStatus contentTypes) as, Unique (Statuses as) -- for consistency with servant-swagger (server would work fine -- without; client is a bit of a corner case, because it dispatches -- the parser based on the status code. with this uniqueness @@ -77,20 +104,13 @@ instance action `addMethodCheck` methodCheck method request `addAcceptCheck` acceptCheck (Proxy @contentTypes) (getAcceptHeader request) - mkProxy :: a -> Proxy a - mkProxy _ = Proxy runAction action' env request cont $ \(output :: Union as) -> do - let encodeResource :: (AllCTRender contentTypes a, HasStatus a) => a -> (Status, Maybe (LBS, LBS)) - encodeResource res = - ( statusOf $ mkProxy res, - handleAcceptH (Proxy @contentTypes) (getAcceptHeader request) res - ) - pickResource :: Union as -> (Status, Maybe (LBS, LBS)) - pickResource = foldMapUnion (Proxy @(IsServerResource contentTypes)) encodeResource - + let cts = Proxy @contentTypes + pickResource :: Union as -> (Status, Maybe (LBS, LBS), [(HeaderName, B.ByteString)]) + pickResource = foldMapUnion (Proxy @(IsServerResourceWithStatus contentTypes)) (encodeResource request cts) case pickResource output of - (_, Nothing) -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does - (status, Just (contentT, body)) -> + (_, Nothing, _) -> FailFatal err406 -- this should not happen (checked before), so we make it fatal if it does + (status, Just (contentT, body), headers) -> let bdy = if allowedMethodHead method request then "" else body - in Route $ responseLBS status ((hContentType, cs contentT) : []) bdy + in Route $ responseLBS status ((hContentType, cs contentT) : headers) bdy diff --git a/servant-server/test/Servant/ServerSpec.hs b/servant-server/test/Servant/ServerSpec.hs index e3dec48e..2eea228d 100644 --- a/servant-server/test/Servant/ServerSpec.hs +++ b/servant-server/test/Servant/ServerSpec.hs @@ -28,6 +28,8 @@ import Data.Maybe (fromMaybe) import Data.Proxy (Proxy (Proxy)) +import Data.SOP + (I (..), NS (..)) import Data.String (fromString) import Data.String.Conversions @@ -53,7 +55,7 @@ import Servant.API NoContent (..), NoContentVerb, NoFraming, OctetStream, Patch, PlainText, Post, Put, QueryFlag, QueryParam, QueryParams, Raw, RemoteHost, ReqBody, SourceIO, StdMethod (..), Stream, Strict, - UVerb, Union, Verb, addHeader) + UVerb, Union, Verb, WithStatus (..), addHeader) import Servant.Server (Context ((:.), EmptyContext), Handler, Server, Tagged (..), emptyServer, err401, err403, err404, respond, serve, @@ -98,6 +100,7 @@ spec = do rawSpec alternativeSpec responseHeadersSpec + uverbResponseHeadersSpec miscCombinatorSpec basicAuthSpec genAuthSpec @@ -684,6 +687,31 @@ responseHeadersSpec = describe "ResponseHeaders" $ do THW.request method "" [(hAccept, "crazy/mime")] "" `shouldRespondWith` 406 +-- }}} +------------------------------------------------------------------------------ +-- * uverbResponseHeaderSpec {{{ +------------------------------------------------------------------------------ +type UVerbHeaderResponse = '[ + WithStatus 200 (Headers '[Header "H1" Int] String), + WithStatus 404 String ] + +type UVerbResponseHeadersApi = + Capture "ok" Bool :> UVerb 'GET '[JSON] UVerbHeaderResponse + +uverbResponseHeadersServer :: Server UVerbResponseHeadersApi +uverbResponseHeadersServer True = pure . Z . I . WithStatus $ addHeader 5 "foo" +uverbResponseHeadersServer False = pure . S . Z . I . WithStatus $ "bar" + +uverbResponseHeadersSpec :: Spec +uverbResponseHeadersSpec = describe "UVerbResponseHeaders" $ do + with (return $ serve (Proxy :: Proxy UVerbResponseHeadersApi) uverbResponseHeadersServer) $ do + + it "includes the headers in the response" $ + THW.request methodGet "/true" [] "" + `shouldRespondWith` "\"foo\"" { matchHeaders = ["H1" <:> "5"] + , matchStatus = 200 + } + -- }}} ------------------------------------------------------------------------------ -- * miscCombinatorSpec {{{ diff --git a/servant/src/Servant/API/ResponseHeaders.hs b/servant/src/Servant/API/ResponseHeaders.hs index b5f98afd..0ec60e22 100644 --- a/servant/src/Servant/API/ResponseHeaders.hs +++ b/servant/src/Servant/API/ResponseHeaders.hs @@ -51,6 +51,9 @@ import Web.HttpApiData import Prelude () import Prelude.Compat +import Servant.API.ContentTypes + (JSON, PlainText, FormUrlEncoded, OctetStream, + MimeRender(..)) import Servant.API.Header (Header) From 0c961f6ebbc930d585b644908fd97ab1384ca2ef Mon Sep 17 00:00:00 2001 From: Alp Date: Tue, 22 Jun 2021 05:54:29 +0200 Subject: [PATCH 108/156] Fix #1405 (#1429) Request bodies are not really supposed to be used in GET requests. --- servant/src/Servant/API/ContentTypes.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant/src/Servant/API/ContentTypes.hs b/servant/src/Servant/API/ContentTypes.hs index d6d200ad..1706a047 100644 --- a/servant/src/Servant/API/ContentTypes.hs +++ b/servant/src/Servant/API/ContentTypes.hs @@ -20,7 +20,7 @@ -- -- Content-Types are used in `ReqBody` and the method combinators: -- --- >>> type MyEndpoint = ReqBody '[JSON, PlainText] Book :> Get '[JSON, PlainText] Book +-- >>> type MyEndpoint = ReqBody '[JSON, PlainText] Book :> Put '[JSON, PlainText] Book -- -- Meaning the endpoint accepts requests of Content-Type @application/json@ -- or @text/plain;charset-utf8@, and returns data in either one of those From cc67b9ec6ea4ea07e830bb037a37f96de10ae677 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Tue, 22 Jun 2021 11:54:50 +0800 Subject: [PATCH 109/156] Allow attoparsec 0.14 (#1408) Builds fine and all tests pass. --- servant/servant.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant/servant.cabal b/servant/servant.cabal index 543887a3..cff41c41 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -97,7 +97,7 @@ library build-depends: base-compat >= 0.10.5 && < 0.12 , aeson >= 1.4.1.0 && < 1.6 - , attoparsec >= 0.13.2.2 && < 0.14 + , attoparsec >= 0.13.2.2 && < 0.15 , bifunctors >= 5.5.3 && < 5.6 , case-insensitive >= 1.2.0.11 && < 1.3 , deepseq >= 1.4.2.0 && < 1.5 From 61111178f05c7b961f0ba814f595ec89de4dc045 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Mon, 5 Apr 2021 17:35:39 +0300 Subject: [PATCH 110/156] Support GHC-9.0.1 --- .github/workflows/master.yml | 3 ++- cabal.project | 16 ++++++++-------- doc/tutorial/tutorial.cabal | 4 ++-- servant-client-core/servant-client-core.cabal | 6 +++--- servant-client/servant-client.cabal | 4 ++-- servant-docs/servant-docs.cabal | 4 ++-- servant-foreign/servant-foreign.cabal | 4 ++-- servant-http-streams/servant-http-streams.cabal | 4 ++-- servant-pipes/example/Main.hs | 2 +- servant-server/servant-server.cabal | 6 +++--- servant/servant.cabal | 8 ++++---- servant/src/Servant/API/UVerb/Union.hs | 2 +- servant/test/Servant/API/StreamSpec.hs | 2 +- 13 files changed, 33 insertions(+), 32 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 6a50181c..afb66384 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - cabal: ["3.2"] + cabal: ["3.4"] ghc: - "8.0.2" - "8.2.2" @@ -21,6 +21,7 @@ jobs: - "8.6.5" - "8.8.4" - "8.10.2" + - "9.0.1" steps: - uses: actions/checkout@v2 diff --git a/cabal.project b/cabal.project index da0f60ad..8b35bf64 100644 --- a/cabal.project +++ b/cabal.project @@ -3,8 +3,7 @@ packages: servant-client/ servant-client-core/ servant-http-streams/ --- Tests failing with Cabal (TODO: investigate) --- servant-docs/ + servant-docs/ servant-foreign/ servant-server/ doc/tutorial/ @@ -26,7 +25,8 @@ packages: doc/cookbook/custom-errors doc/cookbook/basic-streaming doc/cookbook/db-postgres-pool - doc/cookbook/db-sqlite-simple + --doc/cookbook/db-sqlite-simple + -- ^ BROKEN blaze-textual doc/cookbook/file-upload doc/cookbook/generic doc/cookbook/hoist-server-with-context @@ -36,7 +36,8 @@ packages: -- doc/cookbook/sentry -- Commented out because servant-quickcheck currently doesn't build. -- doc/cookbook/testing - doc/cookbook/uverb + --doc/cookbook/uverb + -- ^ BROKEN servant-swagger -> optics-th doc/cookbook/structuring-apis doc/cookbook/using-custom-monad doc/cookbook/using-free-client @@ -64,8 +65,7 @@ write-ghc-environment-files: always allow-newer: servant-pagination-2.2.2:servant allow-newer: servant-pagination-2.2.2:servant-server --- https://github.com/haskell-servant/servant-multipart/pull/41 -allow-newer: servant-multipart:servant -allow-newer: servant-multipart:servant-server -allow-newer: servant-multipart:servant-client-core allow-newer: servant-js:servant + +-- ghc 9 +allow-newer: tdigest:base diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index 82162ffd..a97770cb 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -68,9 +68,9 @@ library , cookie >= 0.4.3 && < 0.5 , js-jquery >= 3.3.1 && < 3.4 , lucid >= 2.9.11 && < 2.10 - , random >= 1.1 && < 1.2 + , 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 diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 61de0ce8..eaec86fa 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -16,7 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 , GHCJS == 8.4 extra-source-files: @@ -50,13 +50,13 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 , text >= 1.2.3.0 && < 1.3 , transformers >= 0.5.2.0 && < 0.6 - , template-haskell >= 2.11.1.0 && < 2.17 + , template-haskell >= 2.11.1.0 && < 2.18 if !impl(ghc >= 8.2) build-depends: diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 7c350257..207bb35e 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -20,7 +20,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md @@ -40,7 +40,7 @@ library -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 87699f37..4b42ba90 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -19,7 +19,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md @@ -41,7 +41,7 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , bytestring >= 0.10.8.1 && < 0.12 , text >= 1.2.3.0 && < 1.3 diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 53c3fb2e..f8b37baa 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -21,7 +21,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2015-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md @@ -41,7 +41,7 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , text >= 1.2.3.0 && < 1.3 -- Servant dependencies diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index ecdee5c6..31d1b8ad 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -20,7 +20,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md @@ -38,7 +38,7 @@ library -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , deepseq >= 1.4.2.0 && < 1.5 diff --git a/servant-pipes/example/Main.hs b/servant-pipes/example/Main.hs index 157ac2e7..8683f651 100644 --- a/servant-pipes/example/Main.hs +++ b/servant-pipes/example/Main.hs @@ -64,7 +64,7 @@ server = fast :<|> slow :<|> readme :<|> proxy readme = liftIO $ do putStrLn "/readme" - return $ P.withFile "README.md" ReadMode PBS.fromHandle + return $ P.withFile "README.md" ReadMode $ \h -> PBS.fromHandle h proxy c = liftIO $ do putStrLn "/proxy" diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index a2a95540..e8007b20 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -23,7 +23,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md @@ -60,7 +60,7 @@ library -- Bundled with GHC: Lower bound to not force re-installs -- text and mtl are bundled starting with GHC-8.4 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , bytestring >= 0.10.8.1 && < 0.12 , containers >= 0.5.7.1 && < 0.7 , mtl >= 2.2.2 && < 2.3 @@ -72,7 +72,7 @@ library -- strict dependency as we re-export 'servant' things. build-depends: servant >= 0.18.2 && < 0.18.3 - , http-api-data >= 0.4.1 && < 0.4.3 + , http-api-data >= 0.4.1 && < 0.4.4 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant/servant.cabal b/servant/servant.cabal index cff41c41..662133f1 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant -version: 0.18.2 +version: 0.18.2.1 synopsis: A family of combinators for defining webservices APIs category: Servant, Web @@ -20,7 +20,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 , GHCJS == 8.4 extra-source-files: @@ -78,7 +78,7 @@ library -- -- note: mtl lower bound is so low because of GHC-7.8 build-depends: - base >= 4.9 && < 4.15 + base >= 4.9 && < 4.16 , bytestring >= 0.10.8.1 && < 0.12 , mtl >= 2.2.2 && < 2.3 , sop-core >= 0.4.0.0 && < 0.6 @@ -89,7 +89,7 @@ library -- We depend (heavily) on the API of these packages: -- i.e. re-export, or allow using without direct dependency build-depends: - http-api-data >= 0.4.1 && < 0.4.3 + http-api-data >= 0.4.1 && < 0.4.4 , singleton-bool >= 0.1.4 && < 0.1.7 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. diff --git a/servant/src/Servant/API/UVerb/Union.hs b/servant/src/Servant/API/UVerb/Union.hs index 774cf628..11d93e74 100644 --- a/servant/src/Servant/API/UVerb/Union.hs +++ b/servant/src/Servant/API/UVerb/Union.hs @@ -144,4 +144,4 @@ _testNubbed :: ( ( Nubbed '[Bool, Int, Int] ~ 'False , Nubbed '[Int, Bool] ~ 'True ) => a) -> a -_testNubbed = id +_testNubbed a = a diff --git a/servant/test/Servant/API/StreamSpec.hs b/servant/test/Servant/API/StreamSpec.hs index 74eac52a..fc5c5046 100644 --- a/servant/test/Servant/API/StreamSpec.hs +++ b/servant/test/Servant/API/StreamSpec.hs @@ -90,7 +90,7 @@ runRenderFrames :: (SourceT Identity a -> SourceT Identity LBS.ByteString) -> [a runRenderFrames f = fmap mconcat . runExcept . runSourceT . f . source runUnrenderFrames :: (SourceT Identity b -> SourceT Identity a) -> [b] -> [Either String a] -runUnrenderFrames f = go . Effect . flip unSourceT return . f . source where +runUnrenderFrames f = go . Effect . (\x -> unSourceT x return) . f . source where go :: StepT Identity a -> [Either String a] go Stop = [] go (Error err) = [Left err] From 4c05338876be1decac66153235a927f693b7abf0 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Mon, 5 Apr 2021 17:41:06 +0300 Subject: [PATCH 111/156] doctest 0.18 --- .github/workflows/master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index afb66384..eee1f47a 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -50,7 +50,7 @@ jobs: - name: Configure run: | - cabal install --ignore-project -j2 doctest --constraint='doctest ^>=0.17' + cabal install --ignore-project -j2 doctest --constraint='doctest ^>=0.18' - name: Build run: | From 6cf2da8b64ef1cef7de753c95508271bdaf454fb Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Mon, 21 Jun 2021 23:47:10 +0200 Subject: [PATCH 112/156] Update GHC 8.10 to 8.10.4 in GitHub actions --- .github/workflows/master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index eee1f47a..8d6f554a 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -20,7 +20,7 @@ jobs: - "8.4.4" - "8.6.5" - "8.8.4" - - "8.10.2" + - "8.10.4" - "9.0.1" steps: From 2eba8866b7d4583e03707fd009f0d3e259353c7e Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Tue, 22 Jun 2021 10:27:01 +0200 Subject: [PATCH 113/156] Fix doctest running in CI --- .github/workflows/master.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 8d6f554a..63e9d4ff 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -50,7 +50,10 @@ jobs: - name: Configure run: | - cabal install --ignore-project -j2 doctest --constraint='doctest ^>=0.18' + # Using separate store-dir because default one already has 'ghc-paths' package installed + # with hardcoded path to ghcup's GHC path (which it was built with). This leads to failure in + # doctest, as it tries to invoke that GHC, and it doesn't exist here. + cabal --store-dir /tmp/cabal-store install --ignore-project -j2 doctest --constraint='doctest ^>=0.18' - name: Build run: | From f527f09ac33b8360782e3797d73276f9772b71b6 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Tue, 22 Jun 2021 15:41:13 +0200 Subject: [PATCH 114/156] continue-on-error for doctest on GHC 9 --- .github/workflows/master.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 63e9d4ff..b4776d41 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -64,6 +64,9 @@ jobs: cabal test all - name: Run doctests + # doctests are broken on GHC 9 due to compiler bug: + # https://gitlab.haskell.org/ghc/ghc/-/issues/19460 + continue-on-error: ${{ matrix.ghc == '9.0.1' }} run: | # Necessary for doctest to be found in $PATH export PATH="$HOME/.cabal/bin:$PATH" From e2b897d3c0655a84551532944f9cffaa9daa8168 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Thu, 24 Jun 2021 00:38:46 +0300 Subject: [PATCH 115/156] Prepare 0.18.3 release (#1430) --- servant-client-core/CHANGELOG.md | 12 ++++++++++++ servant-client-core/servant-client-core.cabal | 4 ++-- servant-client/CHANGELOG.md | 12 ++++++++++++ servant-client/servant-client.cabal | 4 ++-- servant-docs/CHANGELOG.md | 12 ++++++++++++ servant-docs/servant-docs.cabal | 2 +- servant-http-streams/CHANGELOG.md | 9 +++++++++ servant-http-streams/servant-http-streams.cabal | 4 ++-- servant-pipes/CHANGELOG.md | 11 +++++++++++ servant-pipes/servant-pipes.cabal | 2 +- servant-server/CHANGELOG.md | 12 ++++++++++++ servant-server/servant-server.cabal | 4 ++-- servant/CHANGELOG.md | 13 +++++++++++++ servant/servant.cabal | 2 +- 14 files changed, 92 insertions(+), 11 deletions(-) diff --git a/servant-client-core/CHANGELOG.md b/servant-client-core/CHANGELOG.md index 3e34ac95..ce5f1573 100644 --- a/servant-client-core/CHANGELOG.md +++ b/servant-client-core/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.3 +------ + +### Significant changes + +- Add response header support to UVerb (#1420) + +### Other changes + +- Support GHC-9.0.1. +- Bump `bytestring`, `hspec`, `base64-bytestring` and `QuickCheck` dependencies. + 0.18.2 ------ diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index eaec86fa..0a72c8f2 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client-core -version: 0.18.2 +version: 0.18.3 synopsis: Core functionality and class for client function generation for servant APIs category: Servant, Web @@ -64,7 +64,7 @@ library -- Servant dependencies build-depends: - servant >= 0.18.2 && <0.19 + servant >= 0.18.3 && <0.19 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-client/CHANGELOG.md b/servant-client/CHANGELOG.md index f089ecf5..02f27369 100644 --- a/servant-client/CHANGELOG.md +++ b/servant-client/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.3 +------ + +### Significant changes + +- Add response header support to UVerb (#1420) + +### Other changes + +- Support GHC-9.0.1. +- Bump `bytestring`, `hspec`, `http-client` and `QuickCheck` dependencies. + 0.18.2 ------ diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 207bb35e..da1ffffc 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-client -version: 0.18.2 +version: 0.18.3 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -58,7 +58,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18.2 && <0.18.3 + , servant-client-core >= 0.18.3 && <0.18.4 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-docs/CHANGELOG.md b/servant-docs/CHANGELOG.md index 47bc4388..e357ef6c 100644 --- a/servant-docs/CHANGELOG.md +++ b/servant-docs/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-docs/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.11.9 +------ + +### Significant changes + +- Use Capture Description if available (#1423). + +### Other changes + +- Support GHC-9.0.1. +- Bump `bytestring` and `lens` dependencies. + 0.11.8 ------ diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 4b42ba90..0f919ebc 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-docs -version: 0.11.8 +version: 0.11.9 synopsis: generate API docs for your servant webservice category: Servant, Web diff --git a/servant-http-streams/CHANGELOG.md b/servant-http-streams/CHANGELOG.md index 0d2d9f72..40cbf44a 100644 --- a/servant-http-streams/CHANGELOG.md +++ b/servant-http-streams/CHANGELOG.md @@ -1,6 +1,15 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-http-streams/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.3 +------ + +### Other changes + +- Support GHC-9.0.1. +- Fix test suite running in CI. +- Bump `bytestring` and `hspec` dependencies. + 0.18.2 ------ diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 31d1b8ad..ad84615f 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-http-streams -version: 0.18.2 +version: 0.18.3 synopsis: Automatic derivation of querying functions for servant category: Servant, Web @@ -55,7 +55,7 @@ library -- Strict dependency on `servant-client-core` as we re-export things. build-depends: servant == 0.18.* - , servant-client-core >= 0.18.2 && <0.18.3 + , servant-client-core >= 0.18.3 && <0.18.4 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. -- Here can be exceptions if we really need features from the newer versions. diff --git a/servant-pipes/CHANGELOG.md b/servant-pipes/CHANGELOG.md index 113b5c4b..248b5eaf 100644 --- a/servant-pipes/CHANGELOG.md +++ b/servant-pipes/CHANGELOG.md @@ -1,3 +1,14 @@ +0.15.3 +------ + +### Other changes + +- Support GHC-9.0.1. +- Bump `bytestring` dependency. + +0.15.2 +------ + 0.15.1 ------ diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 801f5850..49499870 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-pipes -version: 0.15.2 +version: 0.15.3 synopsis: Servant Stream support for pipes category: Servant, Web, Pipes diff --git a/servant-server/CHANGELOG.md b/servant-server/CHANGELOG.md index 43e03218..b33d9877 100644 --- a/servant-server/CHANGELOG.md +++ b/servant-server/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.3 +------ + +### Significant changes + +- Add response header support to UVerb (#1420) + +### Other changes + +- Support GHC-9.0.1. +- Bump `bytestring`, `hspec` and `base64-bytestring` dependencies. + 0.18.2 ------ diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index e8007b20..685eff72 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-server -version: 0.18.2 +version: 0.18.3 synopsis: A family of combinators for defining webservices APIs and serving them category: Servant, Web @@ -71,7 +71,7 @@ library -- Servant dependencies -- strict dependency as we re-export 'servant' things. build-depends: - servant >= 0.18.2 && < 0.18.3 + servant >= 0.18.3 && < 0.18.4 , http-api-data >= 0.4.1 && < 0.4.4 -- Other dependencies: Lower bound around what is in the latest Stackage LTS. diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index 301081fb..8c58ed7b 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -1,5 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.18.3 +------ + +### Significant changes + +- Add response header support to UVerb (#1420). +- Use Capture Description if available (#1423). + +### Other changes + +- Support GHC-9.0.1. +- Bump `bytestring`, `attoparsec`, `hspec` and `singleton-bool` dependencies. + 0.18.2 ------ diff --git a/servant/servant.cabal b/servant/servant.cabal index 662133f1..da5e6627 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant -version: 0.18.2.1 +version: 0.18.3 synopsis: A family of combinators for defining webservices APIs category: Servant, Web From 21682f6b7212af8527ad99bc15bb3051c6029a68 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Wed, 23 Jun 2021 23:47:01 +0200 Subject: [PATCH 116/156] servant-foreign 0.15.4 --- servant-foreign/CHANGELOG.md | 12 ++++++++++++ servant-foreign/servant-foreign.cabal | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/servant-foreign/CHANGELOG.md b/servant-foreign/CHANGELOG.md index 5f943dca..4c3302ed 100644 --- a/servant-foreign/CHANGELOG.md +++ b/servant-foreign/CHANGELOG.md @@ -1,6 +1,18 @@ [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-foreign/CHANGELOG.md) [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md) +0.15.4 +------ + +### Significant changes + +- Documentation improvements. + +### Other changes + +- Support GHC-9.0.1. +- Bump `lens` and `hspec` dependencies. + 0.15.3 ------ diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index f8b37baa..56911a9f 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -1,6 +1,6 @@ cabal-version: >=1.10 name: servant-foreign -version: 0.15.3 +version: 0.15.4 synopsis: Helpers for generating clients for servant APIs in any programming language category: Servant, Web From 19ec395e66db0232464244c9b4def1883c828828 Mon Sep 17 00:00:00 2001 From: Paolo Capriotti Date: Tue, 13 Jul 2021 17:10:30 +0200 Subject: [PATCH 117/156] 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. --- servant-client/test/Servant/SuccessSpec.hs | 12 +++++------- servant-server/test/Servant/ServerSpec.hs | 7 +++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/servant-client/test/Servant/SuccessSpec.hs b/servant-client/test/Servant/SuccessSpec.hs index b8e94f01..8729caf0 100644 --- a/servant-client/test/Servant/SuccessSpec.hs +++ b/servant-client/test/Servant/SuccessSpec.hs @@ -32,7 +32,6 @@ import Data.Foldable import Data.Maybe (listToMaybe) import Data.Monoid () -import Data.SOP (NS (..), I (..)) import Data.Text (Text) import qualified Network.HTTP.Client as C @@ -43,11 +42,9 @@ import Test.HUnit import Test.QuickCheck import Servant.API - (NoContent (NoContent), WithStatus (WithStatus), getHeaders) + (NoContent (NoContent), WithStatus (WithStatus), getHeaders, Headers(..)) import Servant.Client import qualified Servant.Client.Core.Request as Req -import Servant.Client.Internal.HttpClient - (defaultMakeClientRequest) import Servant.ClientTestUtils import Servant.Test.ComprehensiveAPI @@ -134,9 +131,10 @@ successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do res <- runClient getUVerbRespHeaders baseUrl case res of Left e -> assertFailure $ show e - Right (Z (I (WithStatus val))) -> - getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")] - Right (S _) -> assertFailure "expected first alternative of union" + Right val -> case matchUnion val of + Just (WithStatus val' :: WithStatus 200 (Headers TestHeaders Bool)) + -> getHeaders val' `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")] + Nothing -> assertFailure "unexpected alternative of union" it "Stores Cookie in CookieJar after a redirect" $ \(_, baseUrl) -> do mgr <- C.newManager C.defaultManagerSettings diff --git a/servant-server/test/Servant/ServerSpec.hs b/servant-server/test/Servant/ServerSpec.hs index 2eea228d..39e75cd4 100644 --- a/servant-server/test/Servant/ServerSpec.hs +++ b/servant-server/test/Servant/ServerSpec.hs @@ -7,6 +7,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -freduction-depth=100 #-} module Servant.ServerSpec where @@ -28,8 +29,6 @@ import Data.Maybe (fromMaybe) import Data.Proxy (Proxy (Proxy)) -import Data.SOP - (I (..), NS (..)) import Data.String (fromString) import Data.String.Conversions @@ -699,8 +698,8 @@ type UVerbResponseHeadersApi = Capture "ok" Bool :> UVerb 'GET '[JSON] UVerbHeaderResponse uverbResponseHeadersServer :: Server UVerbResponseHeadersApi -uverbResponseHeadersServer True = pure . Z . I . WithStatus $ addHeader 5 "foo" -uverbResponseHeadersServer False = pure . S . Z . I . WithStatus $ "bar" +uverbResponseHeadersServer True = respond . WithStatus @200 . addHeader @"H1" (5 :: Int) $ ("foo" :: String) +uverbResponseHeadersServer False = respond . WithStatus @404 $ ("bar" :: String) uverbResponseHeadersSpec :: Spec uverbResponseHeadersSpec = describe "UVerbResponseHeaders" $ do From 47bd25266fed2a65d0caed64833e733bb633291d Mon Sep 17 00:00:00 2001 From: Dan Fithian Date: Thu, 19 Aug 2021 07:11:00 -0400 Subject: [PATCH 118/156] Servant docs curl (#1401) servant-dosc: generate sample curl request --- .gitignore | 1 + changelog.d/servant-docs-curl | 16 +++++ servant-docs/example/greet.hs | 8 +-- servant-docs/example/greet.md | 33 +++++++++- servant-docs/src/Servant/Docs/Internal.hs | 73 +++++++++++++++++------ servant-docs/test/Servant/DocsSpec.hs | 1 - 6 files changed, 109 insertions(+), 23 deletions(-) create mode 100644 changelog.d/servant-docs-curl diff --git a/.gitignore b/.gitignore index a74ddee2..6cec8e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ doc/_build doc/venv doc/tutorial/static/api.js doc/tutorial/static/jq.js +shell.nix # nix result* diff --git a/changelog.d/servant-docs-curl b/changelog.d/servant-docs-curl new file mode 100644 index 00000000..96731337 --- /dev/null +++ b/changelog.d/servant-docs-curl @@ -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")]` +} diff --git a/servant-docs/example/greet.hs b/servant-docs/example/greet.hs index ec36c7ca..68edfa5e 100644 --- a/servant-docs/example/greet.hs +++ b/servant-docs/example/greet.hs @@ -75,7 +75,7 @@ intro2 = DocIntro "This title is below the last" -- API specification type TestApi = -- GET /hello/:name?capital={true, false} returns a Greet as JSON or PlainText - "hello" :> Capture "name" Text :> QueryParam "capital" Bool :> Get '[JSON, PlainText] Greet + "hello" :> Capture "name" Text :> Header "X-Num-Fairies" Int :> QueryParam "capital" Bool :> Get '[JSON, PlainText] Greet -- POST /greet with a Greet as JSON in the request body, -- returns a Greet as JSON @@ -93,9 +93,9 @@ testApi = Proxy extra :: ExtraInfo TestApi extra = extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete '[JSON] NoContent)) $ - defAction & headers <>~ ["unicorns"] + defAction & headers <>~ [("X-Num-Unicorns", "1")] & notes <>~ [ DocNote "Title" ["This is some text"] - , DocNote "Second secton" ["And some more"] + , DocNote "Second section" ["And some more"] ] -- Generate the data that lets us have API docs. This @@ -109,4 +109,4 @@ docsGreet :: API docsGreet = docsWith defaultDocOptions [intro1, intro2] extra testApi main :: IO () -main = putStrLn $ markdown docsGreet +main = putStrLn $ markdownWith (defRenderingOptions { _renderCurlBasePath = Just "http://localhost:80" }) docsGreet diff --git a/servant-docs/example/greet.md b/servant-docs/example/greet.md index fea5ce66..1283f628 100644 --- a/servant-docs/example/greet.md +++ b/servant-docs/example/greet.md @@ -51,6 +51,15 @@ You'll also note that multiple intros are possible. "Hello, haskeller" ``` +### Sample Request: + +```bash +curl -XPOST \ + -H "Content-Type: application/json;charset=utf-8" \ + -d "\"HELLO, HASKELLER\"" \ + http://localhost:80/greet +``` + ## DELETE /greet/:greetid ### Title @@ -67,7 +76,7 @@ And some more ### Headers: -- This endpoint is sensitive to the value of the **unicorns** HTTP header. +- This endpoint is sensitive to the value of the **X-Num-Unicorns** HTTP header. ### Response: @@ -85,12 +94,24 @@ And some more ``` +### Sample Request: + +```bash +curl -XDELETE \ + -H "X-Num-Unicorns: 1" \ + http://localhost:80/greet/:greetid +``` + ## GET /hello/:name ### Captures: - *name*: name of the person to greet +### Headers: + +- This endpoint is sensitive to the value of the **X-Num-Fairies** HTTP header. + ### GET Parameters: - capital @@ -120,3 +141,13 @@ And some more ```javascript "Hello, haskeller" ``` + +### Sample Request: + +```bash +curl -XGET \ + -H "X-Num-Fairies: 1729" \ + http://localhost:80/hello/:name +``` + + diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index d19636bf..31c2c141 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -25,8 +25,8 @@ import Control.Applicative import Control.Arrow (second) import Control.Lens - (makeLenses, mapped, over, set, traversed, view, (%~), (&), - (.~), (<>~), (^.), (|>)) + (makeLenses, mapped, each, over, set, to, toListOf, traversed, view, + _1, (%~), (&), (.~), (<>~), (^.), (|>)) import qualified Data.ByteString.Char8 as BSC import Data.ByteString.Lazy.Char8 (ByteString) @@ -59,6 +59,9 @@ import Data.String.Conversions import Data.Text (Text, unpack) import GHC.Generics + (Generic, Rep, K1(K1), M1(M1), U1(U1), V1, + (:*:)((:*:)), (:+:)(L1, R1)) +import qualified GHC.Generics as G import GHC.TypeLits import Servant.API import Servant.API.ContentTypes @@ -295,7 +298,7 @@ defResponse = Response data Action = Action { _authInfo :: [DocAuthentication] -- user supplied info , _captures :: [DocCapture] -- type collected + user supplied info - , _headers :: [Text] -- type collected + , _headers :: [HTTP.Header] -- type collected , _params :: [DocQueryParam] -- type collected + user supplied info , _fragment :: Maybe DocFragment -- type collected + user supplied info , _notes :: [DocNote] -- user supplied @@ -356,12 +359,14 @@ data ShowContentTypes = AllContentTypes -- ^ For each example, show each conten -- -- @since 0.11.1 data RenderingOptions = RenderingOptions - { _requestExamples :: !ShowContentTypes + { _requestExamples :: !ShowContentTypes -- ^ How many content types to display for request body examples? - , _responseExamples :: !ShowContentTypes + , _responseExamples :: !ShowContentTypes -- ^ How many content types to display for response body examples? - , _notesHeading :: !(Maybe String) + , _notesHeading :: !(Maybe String) -- ^ Optionally group all 'notes' together under a common heading. + , _renderCurlBasePath :: !(Maybe String) + -- ^ Optionally render example curl requests under a common base path (e.g. `http://localhost:80`). } deriving (Show) -- | Default API generation options. @@ -373,9 +378,10 @@ data RenderingOptions = RenderingOptions -- @since 0.11.1 defRenderingOptions :: RenderingOptions defRenderingOptions = RenderingOptions - { _requestExamples = AllContentTypes - , _responseExamples = AllContentTypes - , _notesHeading = Nothing + { _requestExamples = AllContentTypes + , _responseExamples = AllContentTypes + , _notesHeading = Nothing + , _renderCurlBasePath = Nothing } -- gimme some lenses @@ -412,7 +418,7 @@ docsWithOptions p = docsFor p (defEndpoint, defAction) -- > extra :: ExtraInfo TestApi -- > extra = -- > extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete)) $ --- > defAction & headers <>~ ["unicorns"] +-- > defAction & headers <>~ [("X-Num-Unicorns", 1)] -- > & notes <>~ [ DocNote "Title" ["This is some text"] -- > , DocNote "Second section" ["And some more"] -- > ] @@ -507,7 +513,7 @@ samples = map ("",) -- | Default sample Generic-based inputs/outputs. defaultSamples :: forall a. (Generic a, GToSample (Rep a)) => Proxy a -> [(Text, a)] -defaultSamples _ = second to <$> gtoSamples (Proxy :: Proxy (Rep a)) +defaultSamples _ = second G.to <$> gtoSamples (Proxy :: Proxy (Rep a)) -- | @'ToSample'@ for Generics. -- @@ -643,7 +649,7 @@ markdown = markdownWith defRenderingOptions -- -- @since 0.11.1 markdownWith :: RenderingOptions -> API -> String -markdownWith RenderingOptions{..} api = unlines $ +markdownWith RenderingOptions{..} api = unlines $ introsStr (api ^. apiIntros) ++ (concatMap (uncurry printEndpoint) . sort . HM.toList $ api ^. apiEndpoints) @@ -654,11 +660,12 @@ markdownWith RenderingOptions{..} api = unlines $ notesStr (action ^. notes) ++ authStr (action ^. authInfo) ++ capturesStr (action ^. captures) ++ - headersStr (action ^. headers) ++ + headersStr (toListOf (headers . each . _1 . to (T.pack . BSC.unpack . CI.original)) action) ++ paramsStr meth (action ^. params) ++ fragmentStr (action ^. fragment) ++ rqbodyStr (action ^. rqtypes) (action ^. rqbody) ++ responseStr (action ^. response) ++ + maybe [] (curlStr endpoint (action ^. headers) (action ^. rqbody)) _renderCurlBasePath ++ [] where str = "## " ++ BSC.unpack meth @@ -814,7 +821,6 @@ markdownWith RenderingOptions{..} api = unlines $ ("text", "css") -> "css" (_, _) -> "" - contentStr mime_type body = "" : "```" <> markdownForType mime_type : @@ -839,6 +845,36 @@ markdownWith RenderingOptions{..} api = unlines $ xs -> formatBodies _responseExamples xs + curlStr :: Endpoint -> [HTTP.Header] -> [(Text, M.MediaType, ByteString)] -> String -> [String] + curlStr endpoint hdrs reqBodies basePath = + [ "### Sample Request:" + , "" + , "```bash" + , "curl -X" ++ BSC.unpack (endpoint ^. method) ++ " \\" + ] <> + maybe [] pure mbMediaTypeStr <> + headersStrs <> + maybe [] pure mbReqBodyStr <> + [ " " ++ basePath ++ showPath (endpoint ^. path) + , "```" + , "" + ] + + where escapeQuotes :: String -> String + escapeQuotes = concatMap $ \c -> case c of + '\"' -> "\\\"" + _ -> [c] + mbReqBody = listToMaybe reqBodies + mbMediaTypeStr = mkMediaTypeStr <$> mbReqBody + headersStrs = mkHeaderStr <$> hdrs + mbReqBodyStr = mkReqBodyStr <$> mbReqBody + mkMediaTypeStr (_, media_type, _) = + " -H \"Content-Type: " ++ show media_type ++ "\" \\" + mkHeaderStr (hdrName, hdrVal) = + " -H \"" ++ escapeQuotes (cs (CI.original hdrName)) ++ ": " ++ + escapeQuotes (cs hdrVal) ++ "\" \\" + mkReqBodyStr (_, _, body) = " -d \"" ++ escapeQuotes (cs body) ++ "\" \\" + -- * Instances -- | The generated docs for @a ':<|>' b@ just appends the docs @@ -977,14 +1013,17 @@ instance {-# OVERLAPPING #-} status = fromInteger $ natVal (Proxy :: Proxy status) p = Proxy :: Proxy a -instance (KnownSymbol sym, HasDocs api) +instance (ToHttpApiData a, ToSample a, KnownSymbol sym, HasDocs api) => HasDocs (Header' mods sym a :> api) where docsFor Proxy (endpoint, action) = docsFor subApiP (endpoint, action') where subApiP = Proxy :: Proxy api - action' = over headers (|> headername) action - headername = T.pack $ symbolVal (Proxy :: Proxy sym) + action' = over headers (|> (headerName, headerVal)) action + headerName = CI.mk . cs $ symbolVal (Proxy :: Proxy sym) + headerVal = case toSample (Proxy :: Proxy a) of + Just x -> cs $ toHeader x + Nothing -> "" instance (KnownSymbol sym, ToParam (QueryParam' mods sym a), HasDocs api) => HasDocs (QueryParam' mods sym a :> api) where diff --git a/servant-docs/test/Servant/DocsSpec.hs b/servant-docs/test/Servant/DocsSpec.hs index 357beed8..4a9efaee 100644 --- a/servant-docs/test/Servant/DocsSpec.hs +++ b/servant-docs/test/Servant/DocsSpec.hs @@ -130,7 +130,6 @@ spec = describe "Servant.Docs" $ do md `shouldContain` "\"dt1field1\":\"field 1\"" it "contains response samples - dt1field2" $ md `shouldContain` "\"dt1field2\":13" - it "contains request body samples" $ md `shouldContain` "17" From 799537f82dec7d9a15af5a04ecdb71208f10911c Mon Sep 17 00:00:00 2001 From: Brandon Chinn Date: Sat, 21 Aug 2021 10:15:02 -0700 Subject: [PATCH 119/156] Add serveWithContextT, ServerContext (#1441) servant-server: add serveWithContexT and ServerContext --- .github/workflows/master.yml | 4 +-- servant-docs/servant-docs.cabal | 2 +- servant-server/src/Servant/Server.hs | 40 +++++++++++++++++++++------- stack.yaml | 2 +- stack.yaml.lock | 19 +++++++++++++ 5 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 stack.yaml.lock diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index b4776d41..d5477cec 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -95,8 +95,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - stack: ["2.3.1"] - ghc: ["8.8.4"] + stack: ["2.7.3"] + ghc: ["8.10.4"] steps: - uses: actions/checkout@v2 diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index 0f919ebc..a26adde9 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -102,7 +102,7 @@ test-suite spec -- Additional dependencies build-depends: - tasty >= 1.1.0.4 && < 1.3, + tasty >= 1.1.0.4 && < 1.5, tasty-golden >= 2.3.2 && < 2.4, tasty-hunit >= 0.10.0.1 && < 0.11, transformers >= 0.5.2.0 && < 0.6 diff --git a/servant-server/src/Servant/Server.hs b/servant-server/src/Servant/Server.hs index 5d40eb6f..fa01daeb 100644 --- a/servant-server/src/Servant/Server.hs +++ b/servant-server/src/Servant/Server.hs @@ -1,9 +1,10 @@ -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} -- | This module lets you implement 'Server's for defined APIs. You'll -- most likely just need 'serve'. @@ -11,6 +12,8 @@ module Servant.Server ( -- * Run a wai application from an API serve , serveWithContext + , serveWithContextT + , ServerContext , -- * Construct a wai Application from an API toApplication @@ -128,6 +131,15 @@ import Servant.Server.UVerb -- * Implementing Servers +-- | Constraints that need to be satisfied on a context for it to be passed to 'serveWithContext'. +-- +-- Typically, this will add default context entries to the context. You shouldn't typically +-- need to worry about these constraints, but if you write a helper function that wraps +-- 'serveWithContext', you might need to include this constraint. +type ServerContext context = + ( HasContextEntry (context .++ DefaultErrorFormatters) ErrorFormatters + ) + -- | 'serve' allows you to implement an API and produce a wai 'Application'. -- -- Example: @@ -157,11 +169,21 @@ serve p = serveWithContext p EmptyContext -- 'defaultErrorFormatters' will always be appended to the end of the passed context, -- but if you pass your own formatter, it will override the default one. serveWithContext :: ( HasServer api context - , HasContextEntry (context .++ DefaultErrorFormatters) ErrorFormatters ) + , ServerContext context + ) => Proxy api -> Context context -> Server api -> Application -serveWithContext p context server = - toApplication (runRouter format404 (route p context (emptyDelayed (Route server)))) +serveWithContext p context = serveWithContextT p context id + +-- | A general 'serve' function that allows you to pass a custom context and hoisting function to +-- apply on all routes. +serveWithContextT :: + forall api context m. + (HasServer api context, ServerContext context) => + Proxy api -> Context context -> (forall x. m x -> Handler x) -> ServerT api m -> Application +serveWithContextT p context toHandler server = + toApplication (runRouter format404 (route p context (emptyDelayed router))) where + router = Route $ hoistServerWithContext p (Proxy :: Proxy context) toHandler server format404 = notFoundErrorFormatter . getContextEntry . mkContextWithErrorFormatter $ context -- | Hoist server implementation. diff --git a/stack.yaml b/stack.yaml index def7c40e..8536a661 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: lts-16.24 +resolver: lts-18.5 packages: - servant-client-core/ - servant-client/ diff --git a/stack.yaml.lock b/stack.yaml.lock new file mode 100644 index 00000000..ce72109b --- /dev/null +++ b/stack.yaml.lock @@ -0,0 +1,19 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: +- completed: + hackage: hspec-wai-0.10.1@sha256:56dd9ec1d56f47ef1946f71f7cbf070e4c285f718cac1b158400ae5e7172ef47,2290 + pantry-tree: + size: 809 + sha256: 17af1c2e709cd84bfda066b9ebb04cdde7f92660c51a1f7401a1e9f766524e93 + original: + hackage: hspec-wai-0.10.1 +snapshots: +- completed: + size: 585817 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/5.yaml + sha256: 22d24d0dacad9c1450b9a174c28d203f9bb482a2a8da9710a2f2a9f4afee2887 + original: lts-18.5 From 3af3129f75045fbbc020584368a9116fdb4b24c8 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Mon, 30 Aug 2021 04:06:09 +0800 Subject: [PATCH 120/156] Allow transformers-compat 0.7 (#1436) Builds fine and all tests pass. --- servant-client/servant-client.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index da1ffffc..498bafbe 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -72,7 +72,7 @@ library , monad-control >= 1.0.2.3 && < 1.1 , semigroupoids >= 5.3.1 && < 5.4 , transformers-base >= 0.4.5.2 && < 0.5 - , transformers-compat >= 0.6.2 && < 0.7 + , transformers-compat >= 0.6.2 && < 0.8 hs-source-dirs: src default-language: Haskell2010 From 993277e8f411dc453122bfd762a857e6526983be Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Fri, 1 Oct 2021 15:15:59 +0200 Subject: [PATCH 121/156] add a coinstraint to http-common to make it build against ghc 8.2.2 --- servant-http-streams/servant-http-streams.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index ad84615f..0db7132d 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -66,7 +66,7 @@ library , http-media >= 0.7.1.3 && < 0.9 , io-streams >= 1.5.0.1 && < 1.6 , http-types >= 0.12.2 && < 0.13 - , http-common >= 0.8.2.0 && < 0.9 + , http-common >= 0.8.2.0 && < 0.8.3 , exceptions >= 0.10.0 && < 0.11 , kan-extensions >= 5.2 && < 5.3 , monad-control >= 1.0.2.3 && < 1.1 From 29aa10176db51bdd79bcf16fdf0fb7f30333732a Mon Sep 17 00:00:00 2001 From: bChiquet Date: Wed, 29 Sep 2021 10:11:28 +0200 Subject: [PATCH 122/156] Document Raw's behaviour when composing APIs --- doc/tutorial/ApiType.lhs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/tutorial/ApiType.lhs b/doc/tutorial/ApiType.lhs index 365c33a7..c631f5d6 100644 --- a/doc/tutorial/ApiType.lhs +++ b/doc/tutorial/ApiType.lhs @@ -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! This library 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 directory. + +``` haskell +type UserAPI15 = "files" :> Raw + -- The raw endpoint is under a directory, + -- 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. +``` From 8b93af3d12261d36240191478e54357d705a5a07 Mon Sep 17 00:00:00 2001 From: bChiquet Date: Wed, 29 Sep 2021 19:59:55 +0200 Subject: [PATCH 123/156] factor in @alp's feedbacks on PR #1455 --- doc/tutorial/ApiType.lhs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/tutorial/ApiType.lhs b/doc/tutorial/ApiType.lhs index c631f5d6..113b8e5f 100644 --- a/doc/tutorial/ApiType.lhs +++ b/doc/tutorial/ApiType.lhs @@ -390,10 +390,10 @@ 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! This library 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. +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 @@ -403,12 +403,12 @@ type UserAPI14 = 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 directory. +definition, or to always have it under a static path. ``` haskell type UserAPI15 = "files" :> Raw - -- The raw endpoint is under a directory, - -- so it won't match /users. + -- 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] From b1a9876dc994bb7bccd5a4c44a0dba0f9eb84dda Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Sun, 12 Sep 2021 10:49:43 +0200 Subject: [PATCH 124/156] unsupporting GHC < 8.6.5 in the nix-shell --- nix/README.md | 17 ++++++++++++++--- nix/shell.nix | 6 ++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/nix/README.md b/nix/README.md index 4dfc519d..7514a6ad 100644 --- a/nix/README.md +++ b/nix/README.md @@ -23,7 +23,18 @@ $ nix-shell nix/shell.nix --argstr compiler ghcHEAD ``` **Possible GHC versions** -- `ghc822Binary` -- `ghc865` +- `ghc865Binary` - `ghc884` -- `ghc8102` - default +- `ghc8104` - default + +### 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 ` \ No newline at end of file diff --git a/nix/shell.nix b/nix/shell.nix index 5fa96923..f5a574f5 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,9 +1,7 @@ -let nixos = fetchTarball { - url = "https://releases.nixos.org/nixos/20.09/nixos-20.09.3505.12d9950bf47/nixexprs.tar.xz"; - sha256 = "0fsl8bsdb8i536pfs4wrp0826h5l84xqlwx32sbz66jg4ykqp9lr"; +let nixos = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/21.05.tar.gz"; }; in -{ compiler ? "ghc8102" +{ compiler ? "ghc8104" , tutorial ? false , pkgs ? import nixos { config = {}; } }: From af7d281ef0056cb36b4fffaea5e0b890b5df62c7 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Sat, 2 Oct 2021 13:09:51 +0200 Subject: [PATCH 125/156] add missing dependencies into shell.nix --- nix/shell.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/shell.nix b/nix/shell.nix index f5a574f5..ebe07722 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -14,7 +14,7 @@ let nixos = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/refs/ in stdenv.mkDerivation { name = "servant-dev"; - buildInputs = [ ghc zlib python3 wget cabal-install postgresql openssl ] + 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) From 1fa18781803d76f65964663f928355b5c578630b Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Sun, 12 Sep 2021 10:49:58 +0200 Subject: [PATCH 126/156] unsupporting GHC < 8.6.5 in the CI --- .github/workflows/master.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index d5477cec..c6a0ed85 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -15,9 +15,6 @@ jobs: os: [ubuntu-latest] cabal: ["3.4"] ghc: - - "8.0.2" - - "8.2.2" - - "8.4.4" - "8.6.5" - "8.8.4" - "8.10.4" From 6e5dffbb91fb2f1a2a60d441d6087e26e3fc4e26 Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Sun, 12 Sep 2021 10:50:53 +0200 Subject: [PATCH 127/156] unsupporting GHC < 8.6.5, removing unecessary imports --- doc/cookbook/curl-mock/CurlMock.lhs | 1 - doc/cookbook/custom-errors/CustomErrors.lhs | 1 - doc/cookbook/uverb/uverb.cabal | 2 -- doc/tutorial/Authentication.lhs | 1 - servant-client-core/src/Servant/Client/Core/BasicAuth.hs | 2 -- servant-client-core/src/Servant/Client/Core/Request.hs | 2 -- servant-client/src/Servant/Client/Internal/HttpClient.hs | 2 -- servant-client/test/Servant/FailSpec.hs | 2 -- servant-docs/src/Servant/Docs/Internal.hs | 2 -- servant-foreign/src/Servant/Foreign/Inflections.hs | 1 - servant-foreign/src/Servant/Foreign/Internal.hs | 2 -- servant-foreign/test/Servant/ForeignSpec.hs | 2 -- servant-http-streams/src/Servant/HttpStreams/Internal.hs | 2 -- servant-http-streams/test/Servant/ClientSpec.hs | 2 -- servant-server/src/Servant/Server/Internal.hs | 2 -- servant-server/src/Servant/Server/Internal/BasicAuth.hs | 2 -- servant-server/test/Servant/Server/ErrorSpec.hs | 2 -- servant/src/Servant/API/Alternative.hs | 2 -- servant/src/Servant/API/Stream.hs | 2 -- servant/src/Servant/Links.hs | 2 -- 20 files changed, 36 deletions(-) diff --git a/doc/cookbook/curl-mock/CurlMock.lhs b/doc/cookbook/curl-mock/CurlMock.lhs index 126349bf..5ed3b1a2 100644 --- a/doc/cookbook/curl-mock/CurlMock.lhs +++ b/doc/cookbook/curl-mock/CurlMock.lhs @@ -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) diff --git a/doc/cookbook/custom-errors/CustomErrors.lhs b/doc/cookbook/custom-errors/CustomErrors.lhs index a8330a5e..4e8b773c 100644 --- a/doc/cookbook/custom-errors/CustomErrors.lhs +++ b/doc/cookbook/custom-errors/CustomErrors.lhs @@ -20,7 +20,6 @@ Extensions and imports: import Data.Aeson import Data.Proxy import Data.Text -import Data.Semigroup import GHC.Generics import Network.Wai import Network.Wai.Handler.Warp diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal index 6c984070..a835a8dc 100644 --- a/doc/cookbook/uverb/uverb.cabal +++ b/doc/cookbook/uverb/uverb.cabal @@ -28,8 +28,6 @@ executable cookbook-uverb , swagger2 , wai , warp - if impl(ghc < 8.6.1) - buildable: False default-language: Haskell2010 ghc-options: -Wall -pgmL markdown-unlit build-tool-depends: markdown-unlit:markdown-unlit diff --git a/doc/tutorial/Authentication.lhs b/doc/tutorial/Authentication.lhs index aa339d54..69bb8f10 100644 --- a/doc/tutorial/Authentication.lhs +++ b/doc/tutorial/Authentication.lhs @@ -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) diff --git a/servant-client-core/src/Servant/Client/Core/BasicAuth.hs b/servant-client-core/src/Servant/Client/Core/BasicAuth.hs index 64862688..3856b6ce 100644 --- a/servant-client-core/src/Servant/Client/Core/BasicAuth.hs +++ b/servant-client-core/src/Servant/Client/Core/BasicAuth.hs @@ -9,8 +9,6 @@ module Servant.Client.Core.BasicAuth ( import Data.ByteString.Base64 (encode) -import Data.Monoid - ((<>)) import Data.Text.Encoding (decodeUtf8) import Servant.API.BasicAuth diff --git a/servant-client-core/src/Servant/Client/Core/Request.hs b/servant-client-core/src/Servant/Client/Core/Request.hs index 0276d46f..9196c795 100644 --- a/servant-client-core/src/Servant/Client/Core/Request.hs +++ b/servant-client-core/src/Servant/Client/Core/Request.hs @@ -35,8 +35,6 @@ import Data.Bitraversable import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Lazy as LBS -import Data.Semigroup - ((<>)) import qualified Data.Sequence as Seq import Data.Text (Text) diff --git a/servant-client/src/Servant/Client/Internal/HttpClient.hs b/servant-client/src/Servant/Client/Internal/HttpClient.hs index 29c209f8..61d51bc4 100644 --- a/servant-client/src/Servant/Client/Internal/HttpClient.hs +++ b/servant-client/src/Servant/Client/Internal/HttpClient.hs @@ -53,8 +53,6 @@ import Data.Maybe (maybe, maybeToList) import Data.Proxy (Proxy (..)) -import Data.Semigroup - ((<>)) import Data.Sequence (fromList) import Data.String diff --git a/servant-client/test/Servant/FailSpec.hs b/servant-client/test/Servant/FailSpec.hs index baec72b6..0abf3e73 100644 --- a/servant-client/test/Servant/FailSpec.hs +++ b/servant-client/test/Servant/FailSpec.hs @@ -21,8 +21,6 @@ import Prelude () import Prelude.Compat import Data.Monoid () -import Data.Semigroup - ((<>)) import qualified Network.HTTP.Types as HTTP import Test.Hspec diff --git a/servant-docs/src/Servant/Docs/Internal.hs b/servant-docs/src/Servant/Docs/Internal.hs index 31c2c141..b989f52b 100644 --- a/servant-docs/src/Servant/Docs/Internal.hs +++ b/servant-docs/src/Servant/Docs/Internal.hs @@ -52,8 +52,6 @@ import Data.Ord (comparing) import Data.Proxy (Proxy (Proxy)) -import Data.Semigroup - (Semigroup (..)) import Data.String.Conversions (cs) import Data.Text diff --git a/servant-foreign/src/Servant/Foreign/Inflections.hs b/servant-foreign/src/Servant/Foreign/Inflections.hs index 42f89927..dcacb4cf 100644 --- a/servant-foreign/src/Servant/Foreign/Inflections.hs +++ b/servant-foreign/src/Servant/Foreign/Inflections.hs @@ -13,7 +13,6 @@ module Servant.Foreign.Inflections import Control.Lens hiding (cons) import qualified Data.Char as C -import Data.Monoid import Data.Text hiding (map) import Prelude hiding diff --git a/servant-foreign/src/Servant/Foreign/Internal.hs b/servant-foreign/src/Servant/Foreign/Internal.hs index 356f9681..ee36315c 100644 --- a/servant-foreign/src/Servant/Foreign/Internal.hs +++ b/servant-foreign/src/Servant/Foreign/Internal.hs @@ -23,8 +23,6 @@ import Control.Lens import Data.Data (Data) import Data.Proxy -import Data.Semigroup - (Semigroup) import Data.String import Data.Text import Data.Text.Encoding diff --git a/servant-foreign/test/Servant/ForeignSpec.hs b/servant-foreign/test/Servant/ForeignSpec.hs index 3baaf7b6..12e52401 100644 --- a/servant-foreign/test/Servant/ForeignSpec.hs +++ b/servant-foreign/test/Servant/ForeignSpec.hs @@ -11,8 +11,6 @@ module Servant.ForeignSpec where -import Data.Monoid - ((<>)) import Data.Proxy import Servant.Foreign import Servant.Test.ComprehensiveAPI diff --git a/servant-http-streams/src/Servant/HttpStreams/Internal.hs b/servant-http-streams/src/Servant/HttpStreams/Internal.hs index 4d2bae2e..aef500b9 100644 --- a/servant-http-streams/src/Servant/HttpStreams/Internal.hs +++ b/servant-http-streams/src/Servant/HttpStreams/Internal.hs @@ -49,8 +49,6 @@ import Data.Maybe (maybeToList) import Data.Proxy (Proxy (..)) -import Data.Semigroup - ((<>)) import Data.Sequence (fromList) import Data.String diff --git a/servant-http-streams/test/Servant/ClientSpec.hs b/servant-http-streams/test/Servant/ClientSpec.hs index 41e7fbe4..b0c752df 100644 --- a/servant-http-streams/test/Servant/ClientSpec.hs +++ b/servant-http-streams/test/Servant/ClientSpec.hs @@ -44,8 +44,6 @@ import Data.Maybe (isJust) import Data.Monoid () import Data.Proxy -import Data.Semigroup - ((<>)) import GHC.Generics (Generic) import qualified Network.HTTP.Types as HTTP diff --git a/servant-server/src/Servant/Server/Internal.hs b/servant-server/src/Servant/Server/Internal.hs index 9579367f..e15102e0 100644 --- a/servant-server/src/Servant/Server/Internal.hs +++ b/servant-server/src/Servant/Server/Internal.hs @@ -46,8 +46,6 @@ import Data.Either (partitionEithers) import Data.Maybe (fromMaybe, isNothing, mapMaybe, maybeToList) -import Data.Semigroup - ((<>)) import Data.String (IsString (..)) import Data.String.Conversions diff --git a/servant-server/src/Servant/Server/Internal/BasicAuth.hs b/servant-server/src/Servant/Server/Internal/BasicAuth.hs index b92e4b02..4b30d897 100644 --- a/servant-server/src/Servant/Server/Internal/BasicAuth.hs +++ b/servant-server/src/Servant/Server/Internal/BasicAuth.hs @@ -12,8 +12,6 @@ import Control.Monad.Trans import qualified Data.ByteString as BS import Data.ByteString.Base64 (decodeLenient) -import Data.Monoid - ((<>)) import Data.Typeable (Typeable) import Data.Word8 diff --git a/servant-server/test/Servant/Server/ErrorSpec.hs b/servant-server/test/Servant/Server/ErrorSpec.hs index 72251b21..e9d880b0 100644 --- a/servant-server/test/Servant/Server/ErrorSpec.hs +++ b/servant-server/test/Servant/Server/ErrorSpec.hs @@ -12,8 +12,6 @@ import Data.Aeson (encode) import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy.Char8 as BCL -import Data.Monoid - ((<>)) import Data.Proxy import Data.String.Conversions (cs) diff --git a/servant/src/Servant/API/Alternative.hs b/servant/src/Servant/API/Alternative.hs index 60152ac1..e87dd394 100644 --- a/servant/src/Servant/API/Alternative.hs +++ b/servant/src/Servant/API/Alternative.hs @@ -16,8 +16,6 @@ import Data.Bifunctor (Bifunctor (..)) import Data.Bitraversable (Bitraversable (..)) -import Data.Semigroup - (Semigroup (..)) import Data.Typeable (Typeable) import Prelude () diff --git a/servant/src/Servant/API/Stream.hs b/servant/src/Servant/API/Stream.hs index 64164f5a..6f6a59cf 100644 --- a/servant/src/Servant/API/Stream.hs +++ b/servant/src/Servant/API/Stream.hs @@ -47,8 +47,6 @@ import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy.Char8 as LBS8 import Data.List.NonEmpty (NonEmpty (..)) -import Data.Monoid - ((<>)) import Data.Proxy (Proxy) import Data.Typeable diff --git a/servant/src/Servant/Links.hs b/servant/src/Servant/Links.hs index bfd47206..50a7ee57 100644 --- a/servant/src/Servant/Links.hs +++ b/servant/src/Servant/Links.hs @@ -127,8 +127,6 @@ module Servant.Links ( import Data.List import Data.Proxy (Proxy (..)) -import Data.Semigroup - ((<>)) import Data.Singletons.Bool (SBool (..), SBoolI (..)) import qualified Data.Text as Text From e56f0092d7f4279be34540a90ad653518a8f3e78 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Sat, 2 Oct 2021 12:56:23 +0200 Subject: [PATCH 128/156] remove tested-with (GHC < 8.6.5) from cabal --- doc/cookbook/basic-auth/basic-auth.cabal | 2 +- doc/cookbook/basic-streaming/basic-streaming.cabal | 2 +- doc/cookbook/curl-mock/curl-mock.cabal | 2 +- doc/cookbook/custom-errors/custom-errors.cabal | 2 +- doc/cookbook/db-postgres-pool/db-postgres-pool.cabal | 2 +- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- doc/cookbook/file-upload/file-upload.cabal | 2 +- doc/cookbook/generic/generic.cabal | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 4 +--- doc/cookbook/https/https.cabal | 2 +- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- doc/cookbook/open-id-connect/OpenIdConnect.cabal | 2 +- doc/cookbook/pagination/pagination.cabal | 2 +- doc/cookbook/sentry/sentry.cabal | 2 +- doc/cookbook/structuring-apis/structuring-apis.cabal | 2 +- doc/cookbook/testing/testing.cabal | 2 +- doc/cookbook/using-custom-monad/using-custom-monad.cabal | 2 +- doc/cookbook/using-free-client/using-free-client.cabal | 2 +- doc/tutorial/tutorial.cabal | 3 --- servant-client-core/servant-client-core.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-conduit/servant-conduit.cabal | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-machines/servant-machines.cabal | 2 +- servant-pipes/servant-pipes.cabal | 2 +- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 29 files changed, 28 insertions(+), 33 deletions(-) diff --git a/doc/cookbook/basic-auth/basic-auth.cabal b/doc/cookbook/basic-auth/basic-auth.cabal index d3f41059..50354167 100644 --- a/doc/cookbook/basic-auth/basic-auth.cabal +++ b/doc/cookbook/basic-auth/basic-auth.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-basic-auth main-is: BasicAuth.lhs diff --git a/doc/cookbook/basic-streaming/basic-streaming.cabal b/doc/cookbook/basic-streaming/basic-streaming.cabal index bfbf5331..0d872c0a 100644 --- a/doc/cookbook/basic-streaming/basic-streaming.cabal +++ b/doc/cookbook/basic-streaming/basic-streaming.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-basic-streaming main-is: Streaming.lhs diff --git a/doc/cookbook/curl-mock/curl-mock.cabal b/doc/cookbook/curl-mock/curl-mock.cabal index c0322413..fb78f58c 100644 --- a/doc/cookbook/curl-mock/curl-mock.cabal +++ b/doc/cookbook/curl-mock/curl-mock.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbock-curl-mock main-is: CurlMock.lhs diff --git a/doc/cookbook/custom-errors/custom-errors.cabal b/doc/cookbook/custom-errors/custom-errors.cabal index d43b5102..f9f87df8 100644 --- a/doc/cookbook/custom-errors/custom-errors.cabal +++ b/doc/cookbook/custom-errors/custom-errors.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-custom-errors main-is: CustomErrors.lhs diff --git a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal index 8d0a6eb0..b7c86034 100644 --- a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal +++ b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-db-postgres-pool main-is: PostgresPool.lhs diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index 108b85c8..12fe44cc 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-db-sqlite-simple main-is: DBConnection.lhs diff --git a/doc/cookbook/file-upload/file-upload.cabal b/doc/cookbook/file-upload/file-upload.cabal index d589912c..9ae526d6 100644 --- a/doc/cookbook/file-upload/file-upload.cabal +++ b/doc/cookbook/file-upload/file-upload.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-file-upload main-is: FileUpload.lhs diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal index 5081a981..d58d1e9c 100644 --- a/doc/cookbook/generic/generic.cabal +++ b/doc/cookbook/generic/generic.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-using-custom-monad main-is: Generic.lhs diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index 7db1ab7e..708f9f52 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -11,7 +11,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-hoist-server-with-context main-is: HoistServerWithContext.lhs @@ -32,8 +32,6 @@ executable cookbook-hoist-server-with-context , http-types >= 0.12 , bytestring >= 0.10.4 , mtl - if impl(ghc < 8.2.1) - buildable: False default-language: Haskell2010 ghc-options: -Wall -pgmL markdown-unlit build-tool-depends: markdown-unlit:markdown-unlit diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index e2ca9c15..6541ffc3 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-https main-is: Https.lhs diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index dcbb95ba..daa605c2 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -11,7 +11,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-jwt-and-basic-auth main-is: JWTAndBasicAuth.lhs diff --git a/doc/cookbook/open-id-connect/OpenIdConnect.cabal b/doc/cookbook/open-id-connect/OpenIdConnect.cabal index eae9fb7e..a7c3d35a 100644 --- a/doc/cookbook/open-id-connect/OpenIdConnect.cabal +++ b/doc/cookbook/open-id-connect/OpenIdConnect.cabal @@ -8,7 +8,7 @@ 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 diff --git a/doc/cookbook/pagination/pagination.cabal b/doc/cookbook/pagination/pagination.cabal index 2e021dc0..d184a968 100644 --- a/doc/cookbook/pagination/pagination.cabal +++ b/doc/cookbook/pagination/pagination.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-pagination main-is: Pagination.lhs diff --git a/doc/cookbook/sentry/sentry.cabal b/doc/cookbook/sentry/sentry.cabal index 41750a45..455682ac 100644 --- a/doc/cookbook/sentry/sentry.cabal +++ b/doc/cookbook/sentry/sentry.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-sentry main-is: Sentry.lhs diff --git a/doc/cookbook/structuring-apis/structuring-apis.cabal b/doc/cookbook/structuring-apis/structuring-apis.cabal index 431950f1..1ef71a56 100644 --- a/doc/cookbook/structuring-apis/structuring-apis.cabal +++ b/doc/cookbook/structuring-apis/structuring-apis.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-structuring-apis main-is: StructuringApis.lhs diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index 6d9c59a7..818b5e21 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -10,7 +10,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-testing main-is: Testing.lhs diff --git a/doc/cookbook/using-custom-monad/using-custom-monad.cabal b/doc/cookbook/using-custom-monad/using-custom-monad.cabal index 022a1ad8..5b00e17d 100644 --- a/doc/cookbook/using-custom-monad/using-custom-monad.cabal +++ b/doc/cookbook/using-custom-monad/using-custom-monad.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-using-custom-monad main-is: UsingCustomMonad.lhs diff --git a/doc/cookbook/using-free-client/using-free-client.cabal b/doc/cookbook/using-free-client/using-free-client.cabal index 51ac1fc0..8fcd4bb1 100644 --- a/doc/cookbook/using-free-client/using-free-client.cabal +++ b/doc/cookbook/using-free-client/using-free-client.cabal @@ -8,7 +8,7 @@ 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.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 executable cookbook-using-free-client main-is: UsingFreeClient.lhs diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index a97770cb..bf484d40 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -13,9 +13,6 @@ 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.3, GHC ==8.10.1 extra-source-files: diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index 0a72c8f2..b1008f36 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -16,7 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 , GHCJS == 8.4 extra-source-files: diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 498bafbe..8f69a4ad 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -20,7 +20,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 53e50bc9..2958e47d 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -16,7 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2018-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index a26adde9..f0d7949d 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -19,7 +19,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index 56911a9f..b9c01048 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -21,7 +21,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2015-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index 0db7132d..b7798f1c 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -20,7 +20,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 2ce1f564..43bc7b46 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -16,7 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2018-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 49499870..599268f1 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -16,7 +16,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2018-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 extra-source-files: CHANGELOG.md diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 685eff72..0b3736e8 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -23,7 +23,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 extra-source-files: CHANGELOG.md diff --git a/servant/servant.cabal b/servant/servant.cabal index da5e6627..8dd79c75 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -20,7 +20,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple -tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 , GHCJS == 8.4 extra-source-files: From e5f1604a9d8c0e415193fc8ba168ff2e73907a60 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Sat, 2 Oct 2021 12:57:16 +0200 Subject: [PATCH 129/156] removing Makefile deprecated with its GHC version --- Makefile | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 2caf4c05..00000000 --- a/Makefile +++ /dev/null @@ -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 () { 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) From a4aacc94751430321950e2f259cb3c0352e0c7e7 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Sat, 2 Oct 2021 13:20:05 +0200 Subject: [PATCH 130/156] A new version of hashable isn't compitable with our ghcjs. A max bound version is needed in the Cabal file --- cabal.ghcjs.project | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cabal.ghcjs.project b/cabal.ghcjs.project index 11ceb957..72902332 100644 --- a/cabal.ghcjs.project +++ b/cabal.ghcjs.project @@ -7,3 +7,5 @@ packages: -- we need to tell cabal we are using GHCJS compiler: ghcjs tests: True + +constraints: hashable <=1.3.3.0 From 2ea6664124a7701035964f1477208aa9658256cd Mon Sep 17 00:00:00 2001 From: akhesaCaro Date: Tue, 7 Sep 2021 14:21:13 +0200 Subject: [PATCH 131/156] GHC9 mention in Nix README. --- nix/README.md | 1 + nix/shell.nix | 1 + 2 files changed, 2 insertions(+) diff --git a/nix/README.md b/nix/README.md index 7514a6ad..f865a383 100644 --- a/nix/README.md +++ b/nix/README.md @@ -26,6 +26,7 @@ $ nix-shell nix/shell.nix --argstr compiler ghcHEAD - `ghc865Binary` - `ghc884` - `ghc8104` - default +- `ghc901` ### Cabal users diff --git a/nix/shell.nix b/nix/shell.nix index ebe07722..715004f6 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,4 +1,5 @@ let nixos = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/21.05.tar.gz"; + sha256 = "sha256:1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36"; }; in { compiler ? "ghc8104" From 61d097db44c083b15df5907fd91612ef4987b0dc Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Sat, 2 Oct 2021 18:07:03 +0200 Subject: [PATCH 132/156] uncomment uverb coobook and include it from building against GHC >= 9 --- cabal.project | 3 +-- doc/cookbook/uverb/uverb.cabal | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 8b35bf64..da6772b6 100644 --- a/cabal.project +++ b/cabal.project @@ -36,8 +36,7 @@ packages: -- doc/cookbook/sentry -- Commented out because servant-quickcheck currently doesn't build. -- doc/cookbook/testing - --doc/cookbook/uverb - -- ^ BROKEN servant-swagger -> optics-th + doc/cookbook/uverb doc/cookbook/structuring-apis doc/cookbook/using-custom-monad doc/cookbook/using-free-client diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal index a835a8dc..15f921bb 100644 --- a/doc/cookbook/uverb/uverb.cabal +++ b/doc/cookbook/uverb/uverb.cabal @@ -28,6 +28,8 @@ executable cookbook-uverb , 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 From 9be55b3ba3c25e5223ed41c5d0d64ce970d4493f Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Sat, 2 Oct 2021 18:13:24 +0200 Subject: [PATCH 133/156] uncomment db-sqlite-simple cookbook to add it to the building plan --- cabal.project | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index da6772b6..3e57f34d 100644 --- a/cabal.project +++ b/cabal.project @@ -25,8 +25,7 @@ packages: doc/cookbook/custom-errors doc/cookbook/basic-streaming doc/cookbook/db-postgres-pool - --doc/cookbook/db-sqlite-simple - -- ^ BROKEN blaze-textual + doc/cookbook/db-sqlite-simple doc/cookbook/file-upload doc/cookbook/generic doc/cookbook/hoist-server-with-context From c011f12d24e8f928da45f9d1bd9b03054be3085d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Choutri?= Date: Sat, 2 Oct 2021 19:16:32 +0200 Subject: [PATCH 134/156] Use GHC 8.10.7 for CI and in Cabal metadata Sponsored by: Scrive AB --- .github/workflows/master.yml | 2 +- doc/cookbook/basic-auth/basic-auth.cabal | 2 +- doc/cookbook/basic-streaming/basic-streaming.cabal | 2 +- doc/cookbook/curl-mock/curl-mock.cabal | 2 +- doc/cookbook/custom-errors/custom-errors.cabal | 2 +- doc/cookbook/db-postgres-pool/db-postgres-pool.cabal | 2 +- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- doc/cookbook/file-upload/file-upload.cabal | 2 +- doc/cookbook/generic/generic.cabal | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 2 +- doc/cookbook/https/https.cabal | 2 +- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- doc/cookbook/pagination/pagination.cabal | 2 +- doc/cookbook/sentry/sentry.cabal | 2 +- doc/cookbook/structuring-apis/structuring-apis.cabal | 2 +- doc/cookbook/testing/testing.cabal | 2 +- doc/cookbook/using-custom-monad/using-custom-monad.cabal | 2 +- doc/cookbook/using-free-client/using-free-client.cabal | 2 +- doc/cookbook/uverb/uverb.cabal | 2 +- doc/tutorial/tutorial.cabal | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index c6a0ed85..2eea692c 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -17,7 +17,7 @@ jobs: ghc: - "8.6.5" - "8.8.4" - - "8.10.4" + - "8.10.7" - "9.0.1" steps: diff --git a/doc/cookbook/basic-auth/basic-auth.cabal b/doc/cookbook/basic-auth/basic-auth.cabal index 50354167..a8551ca3 100644 --- a/doc/cookbook/basic-auth/basic-auth.cabal +++ b/doc/cookbook/basic-auth/basic-auth.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-basic-auth main-is: BasicAuth.lhs diff --git a/doc/cookbook/basic-streaming/basic-streaming.cabal b/doc/cookbook/basic-streaming/basic-streaming.cabal index 0d872c0a..07c1e92c 100644 --- a/doc/cookbook/basic-streaming/basic-streaming.cabal +++ b/doc/cookbook/basic-streaming/basic-streaming.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-basic-streaming main-is: Streaming.lhs diff --git a/doc/cookbook/curl-mock/curl-mock.cabal b/doc/cookbook/curl-mock/curl-mock.cabal index fb78f58c..0bc9f9ce 100644 --- a/doc/cookbook/curl-mock/curl-mock.cabal +++ b/doc/cookbook/curl-mock/curl-mock.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbock-curl-mock main-is: CurlMock.lhs diff --git a/doc/cookbook/custom-errors/custom-errors.cabal b/doc/cookbook/custom-errors/custom-errors.cabal index f9f87df8..972f505e 100644 --- a/doc/cookbook/custom-errors/custom-errors.cabal +++ b/doc/cookbook/custom-errors/custom-errors.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-custom-errors main-is: CustomErrors.lhs diff --git a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal index b7c86034..9f4245de 100644 --- a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal +++ b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-db-postgres-pool main-is: PostgresPool.lhs diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index 12fe44cc..92b84a8b 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-db-sqlite-simple main-is: DBConnection.lhs diff --git a/doc/cookbook/file-upload/file-upload.cabal b/doc/cookbook/file-upload/file-upload.cabal index 9ae526d6..76d54550 100644 --- a/doc/cookbook/file-upload/file-upload.cabal +++ b/doc/cookbook/file-upload/file-upload.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-file-upload main-is: FileUpload.lhs diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal index d58d1e9c..2a8b93cf 100644 --- a/doc/cookbook/generic/generic.cabal +++ b/doc/cookbook/generic/generic.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-using-custom-monad main-is: Generic.lhs diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index 708f9f52..a73a80ce 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -11,7 +11,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com category: Servant build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-hoist-server-with-context main-is: HoistServerWithContext.lhs diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index 6541ffc3..12e33853 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-https main-is: Https.lhs diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index daa605c2..55632e90 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -11,7 +11,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com category: Servant build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-jwt-and-basic-auth main-is: JWTAndBasicAuth.lhs diff --git a/doc/cookbook/pagination/pagination.cabal b/doc/cookbook/pagination/pagination.cabal index d184a968..697c8075 100644 --- a/doc/cookbook/pagination/pagination.cabal +++ b/doc/cookbook/pagination/pagination.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-pagination main-is: Pagination.lhs diff --git a/doc/cookbook/sentry/sentry.cabal b/doc/cookbook/sentry/sentry.cabal index 455682ac..510606c3 100644 --- a/doc/cookbook/sentry/sentry.cabal +++ b/doc/cookbook/sentry/sentry.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-sentry main-is: Sentry.lhs diff --git a/doc/cookbook/structuring-apis/structuring-apis.cabal b/doc/cookbook/structuring-apis/structuring-apis.cabal index 1ef71a56..9d1ea4bb 100644 --- a/doc/cookbook/structuring-apis/structuring-apis.cabal +++ b/doc/cookbook/structuring-apis/structuring-apis.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-structuring-apis main-is: StructuringApis.lhs diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index 818b5e21..e7ba158b 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -10,7 +10,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com category: Servant build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-testing main-is: Testing.lhs diff --git a/doc/cookbook/using-custom-monad/using-custom-monad.cabal b/doc/cookbook/using-custom-monad/using-custom-monad.cabal index 5b00e17d..5bfa3629 100644 --- a/doc/cookbook/using-custom-monad/using-custom-monad.cabal +++ b/doc/cookbook/using-custom-monad/using-custom-monad.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-using-custom-monad main-is: UsingCustomMonad.lhs diff --git a/doc/cookbook/using-free-client/using-free-client.cabal b/doc/cookbook/using-free-client/using-free-client.cabal index 8fcd4bb1..b02ba118 100644 --- a/doc/cookbook/using-free-client/using-free-client.cabal +++ b/doc/cookbook/using-free-client/using-free-client.cabal @@ -8,7 +8,7 @@ author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.3, GHC ==8.10.7 executable cookbook-using-free-client main-is: UsingFreeClient.lhs diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal index 15f921bb..176983fb 100644 --- a/doc/cookbook/uverb/uverb.cabal +++ b/doc/cookbook/uverb/uverb.cabal @@ -10,7 +10,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com category: Servant build-type: Simple cabal-version: >=1.10 -tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.1 +tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.7 executable cookbook-uverb main-is: UVerb.lhs diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index bf484d40..9d59c825 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -14,7 +14,7 @@ build-type: Simple cabal-version: >=1.10 tested-with: GHC==8.6.5 - GHC==8.8.3, GHC ==8.10.1 + GHC==8.8.3, GHC ==8.10.7 extra-source-files: static/index.html static/ui.js From 9666f1956b95e23d65276909e1f06353828cb4c0 Mon Sep 17 00:00:00 2001 From: Ian Shipman Date: Fri, 27 Aug 2021 17:57:37 -0500 Subject: [PATCH 135/156] 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 --- .../src/Servant/Client/Core/HasClient.hs | 12 +++++-- .../src/Servant/Client/Core/Request.hs | 6 ++-- .../src/Servant/Client/Internal/HttpClient.hs | 13 ++++++-- .../test/Servant/ClientTestUtils.hs | 32 +++++++++++++++++-- servant-client/test/Servant/SuccessSpec.hs | 9 +++++- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/servant-client-core/src/Servant/Client/Core/HasClient.hs b/servant-client-core/src/Servant/Client/Core/HasClient.hs index 5f7ad3b3..d598bf66 100644 --- a/servant-client-core/src/Servant/Client/Core/HasClient.hs +++ b/servant-client-core/src/Servant/Client/Core/HasClient.hs @@ -33,6 +33,9 @@ import Control.Arrow (left, (+++)) import Control.Monad (unless) +import qualified Data.ByteString as BS +import Data.ByteString.Builder + (toLazyByteString) import qualified Data.ByteString.Lazy as BL import Data.Either (partitionEithers) @@ -76,7 +79,7 @@ import Servant.API ReflectMethod (..), RemoteHost, ReqBody', SBoolI, Stream, StreamBody', Summary, ToHttpApiData, ToSourceIO (..), Vault, Verb, WithNamedContext, WithStatus (..), contentType, getHeadersHList, - getResponse, toQueryParam, toUrlPiece) + getResponse, toEncodedUrlPiece, toUrlPiece) import Servant.API.ContentTypes (contentTypes, AllMime (allMime), AllMimeUnrender (allMimeUnrender)) import Servant.API.TypeLevel (FragmentUnique, AtLeastOneFragment) @@ -554,7 +557,7 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequire (Proxy :: Proxy mods) add (maybe req add) mparam where add :: a -> Request - add param = appendToQueryString pname (Just $ toQueryParam param) req + add param = appendToQueryString pname (Just $ encodeQueryParam param) req pname :: Text pname = pack $ symbolVal (Proxy :: Proxy sym) @@ -562,6 +565,9 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequire hoistClientMonad pm _ f cl = \arg -> hoistClientMonad pm (Proxy :: Proxy api) f (cl arg) +encodeQueryParam :: ToHttpApiData a => a -> BS.ByteString +encodeQueryParam = BL.toStrict . toLazyByteString . toEncodedUrlPiece + -- | If you use a 'QueryParams' in one of your endpoints in your API, -- the corresponding querying function will automatically take -- an additional argument, a list of values of the type specified @@ -603,7 +609,7 @@ instance (KnownSymbol sym, ToHttpApiData a, HasClient m api) ) where pname = pack $ symbolVal (Proxy :: Proxy sym) - paramlist' = map (Just . toQueryParam) paramlist + paramlist' = map (Just . encodeQueryParam) paramlist hoistClientMonad pm _ f cl = \as -> hoistClientMonad pm (Proxy :: Proxy api) f (cl as) diff --git a/servant-client-core/src/Servant/Client/Core/Request.hs b/servant-client-core/src/Servant/Client/Core/Request.hs index 9196c795..bdc3e382 100644 --- a/servant-client-core/src/Servant/Client/Core/Request.hs +++ b/servant-client-core/src/Servant/Client/Core/Request.hs @@ -145,13 +145,13 @@ appendToPath :: Text -> Request -> Request appendToPath p req = req { requestPath = requestPath req <> "/" <> toEncodedUrlPiece p } -appendToQueryString :: Text -- ^ param name - -> Maybe Text -- ^ param value +appendToQueryString :: Text -- ^ param name + -> Maybe BS.ByteString -- ^ param value -> Request -> Request appendToQueryString pname pvalue req = req { requestQueryString = requestQueryString req - Seq.|> (encodeUtf8 pname, encodeUtf8 <$> pvalue)} + Seq.|> (encodeUtf8 pname, pvalue)} addHeader :: ToHttpApiData a => HeaderName -> a -> Request -> Request addHeader name val req diff --git a/servant-client/src/Servant/Client/Internal/HttpClient.hs b/servant-client/src/Servant/Client/Internal/HttpClient.hs index 61d51bc4..a2c6864d 100644 --- a/servant-client/src/Servant/Client/Internal/HttpClient.hs +++ b/servant-client/src/Servant/Client/Internal/HttpClient.hs @@ -46,7 +46,7 @@ import qualified Data.ByteString.Lazy as BSL import Data.Either (either) import Data.Foldable - (toList) + (foldl',toList) import Data.Functor.Alt (Alt (..)) import Data.Maybe @@ -63,7 +63,7 @@ import GHC.Generics import Network.HTTP.Media (renderHeader) import Network.HTTP.Types - (hContentType, renderQuery, statusCode, Status) + (hContentType, renderQuery, statusCode, urlEncode, Status) import Servant.Client.Core import qualified Network.HTTP.Client as Client @@ -238,7 +238,7 @@ defaultMakeClientRequest burl r = Client.defaultRequest , Client.path = BSL.toStrict $ fromString (baseUrlPath burl) <> toLazyByteString (requestPath r) - , Client.queryString = renderQuery True . toList $ requestQueryString r + , Client.queryString = buildQueryString . toList $ requestQueryString r , Client.requestHeaders = maybeToList acceptHdr ++ maybeToList contentTypeHdr ++ headers , Client.requestBody = body @@ -289,6 +289,13 @@ defaultMakeClientRequest burl r = Client.defaultRequest Http -> False Https -> True + -- Query string builder which does not do any encoding + buildQueryString = ("?" <>) . foldl' addQueryParam mempty + + addQueryParam qs (k, v) = + qs <> (if BS.null qs then mempty else "&") <> urlEncode True k <> foldMap ("=" <>) v + + catchConnectionError :: IO a -> IO (Either ClientError a) catchConnectionError action = catch (Right <$> action) $ \e -> diff --git a/servant-client/test/Servant/ClientTestUtils.hs b/servant-client/test/Servant/ClientTestUtils.hs index 842712e1..198c6462 100644 --- a/servant-client/test/Servant/ClientTestUtils.hs +++ b/servant-client/test/Servant/ClientTestUtils.hs @@ -24,9 +24,15 @@ import Prelude.Compat import Control.Concurrent (ThreadId, forkIO, killThread) +import Control.Monad + (join) import Control.Monad.Error.Class (throwError) import Data.Aeson +import Data.ByteString + (ByteString) +import Data.ByteString.Builder + (byteString) import qualified Data.ByteString.Lazy as LazyByteString import Data.Char (chr, isPrint) @@ -54,10 +60,10 @@ import Web.FormUrlEncoded import Servant.API ((:<|>) ((:<|>)), (:>), AuthProtect, BasicAuth, BasicAuthData (..), Capture, CaptureAll, DeleteNoContent, - EmptyAPI, FormUrlEncoded, Fragment, Get, Header, Headers, + EmptyAPI, FormUrlEncoded, Fragment, FromHttpApiData (..), Get, Header, Headers, JSON, MimeRender (mimeRender), MimeUnrender (mimeUnrender), NoContent (NoContent), PlainText, Post, QueryFlag, QueryParam, - QueryParams, Raw, ReqBody, StdMethod (GET), UVerb, Union, + QueryParams, Raw, ReqBody, StdMethod (GET), ToHttpApiData (..), UVerb, Union, WithStatus (WithStatus), addHeader) import Servant.Client import qualified Servant.Client.Core.Auth as Auth @@ -109,6 +115,10 @@ type Api = :<|> "captureAll" :> CaptureAll "names" String :> Get '[JSON] [Person] :<|> "body" :> ReqBody '[FormUrlEncoded,JSON] Person :> Post '[JSON] Person :<|> "param" :> QueryParam "name" String :> Get '[FormUrlEncoded,JSON] Person + -- This endpoint makes use of a 'Raw' server because it is not currently + -- possible to handle arbitrary binary query param values with + -- @servant-server@ + :<|> "param-binary" :> QueryParam "payload" UrlEncodedByteString :> Raw :<|> "params" :> QueryParams "names" String :> Get '[JSON] [Person] :<|> "flag" :> QueryFlag "flag" :> Get '[JSON] Bool :<|> "fragment" :> Fragment String :> Get '[JSON] Person @@ -143,6 +153,7 @@ getCapture :: String -> ClientM Person getCaptureAll :: [String] -> ClientM [Person] getBody :: Person -> ClientM Person getQueryParam :: Maybe String -> ClientM Person +getQueryParamBinary :: Maybe UrlEncodedByteString -> HTTP.Method -> ClientM Response getQueryParams :: [String] -> ClientM [Person] getQueryFlag :: Bool -> ClientM Bool getFragment :: ClientM Person @@ -167,6 +178,7 @@ getRoot :<|> getCaptureAll :<|> getBody :<|> getQueryParam + :<|> getQueryParamBinary :<|> getQueryParams :<|> getQueryFlag :<|> getFragment @@ -194,6 +206,13 @@ server = serve api ( Just "alice" -> return alice Just n -> throwError $ ServerError 400 (n ++ " not found") "" [] Nothing -> throwError $ ServerError 400 "missing parameter" "" []) + :<|> const (Tagged $ \request respond -> + respond . maybe (Wai.responseLBS HTTP.notFound404 [] "Missing: payload") + (Wai.responseLBS HTTP.ok200 [] . LazyByteString.fromStrict) + . join + . lookup "payload" + $ Wai.queryString request + ) :<|> (\ names -> return (zipWith Person names [0..])) :<|> return :<|> return alice @@ -310,3 +329,12 @@ pathGen = fmap NonEmpty path filter (not . (`elem` ("?%[]/#;" :: String))) $ filter isPrint $ map chr [0..127] + +newtype UrlEncodedByteString = UrlEncodedByteString { unUrlEncodedByteString :: ByteString } + +instance ToHttpApiData UrlEncodedByteString where + toEncodedUrlPiece = byteString . HTTP.urlEncode True . unUrlEncodedByteString + toUrlPiece = decodeUtf8 . HTTP.urlEncode True . unUrlEncodedByteString + +instance FromHttpApiData UrlEncodedByteString where + parseUrlPiece = pure . UrlEncodedByteString . HTTP.urlDecode True . encodeUtf8 diff --git a/servant-client/test/Servant/SuccessSpec.hs b/servant-client/test/Servant/SuccessSpec.hs index 8729caf0..6b9f3bd0 100644 --- a/servant-client/test/Servant/SuccessSpec.hs +++ b/servant-client/test/Servant/SuccessSpec.hs @@ -22,11 +22,13 @@ import Prelude () import Prelude.Compat import Control.Arrow - (left) + ((+++), left) import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TVar (newTVar, readTVar) +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BL import Data.Foldable (forM_, toList) import Data.Maybe @@ -93,6 +95,11 @@ successSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do Left (FailureResponse _ r) <- runClient (getQueryParam (Just "bob")) baseUrl responseStatusCode r `shouldBe` HTTP.Status 400 "bob not found" + it "Servant.API.QueryParam binary data" $ \(_, baseUrl) -> do + let payload = BS.pack [0, 1, 2, 4, 8, 16, 32, 64, 128] + apiCall = getQueryParamBinary (Just $ UrlEncodedByteString payload) HTTP.methodGet + (show +++ responseBody) <$> runClient apiCall baseUrl `shouldReturn` Right (BL.fromStrict payload) + it "Servant.API.QueryParam.QueryParams" $ \(_, baseUrl) -> do left show <$> runClient (getQueryParams []) baseUrl `shouldReturn` Right [] left show <$> runClient (getQueryParams ["alice", "bob"]) baseUrl From d5e439e56b934727faa107b2828904fefa728ef9 Mon Sep 17 00:00:00 2001 From: Ian Shipman Date: Sat, 28 Aug 2021 09:40:24 -0500 Subject: [PATCH 136/156] Updates changelog --- changelog.d/1432 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 changelog.d/1432 diff --git a/changelog.d/1432 b/changelog.d/1432 new file mode 100644 index 00000000..f88207b9 --- /dev/null +++ b/changelog.d/1432 @@ -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. +} From 910a3ae7ec94d2ec671969a1545fbd3c6e1e712a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Sat, 9 Oct 2021 13:27:01 +0200 Subject: [PATCH 137/156] 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. --- .github/run-ghcjs-tests.sh | 14 +++++++++++++ .github/workflows/master.yml | 40 ++++++++++++++++++++++++++++++++---- cabal.ghcjs.project | 4 +++- ghcjs.nix | 22 ++++++++++++++++++++ nix/nixpkgs.nix | 4 ++++ nix/shell.nix | 6 +----- 6 files changed, 80 insertions(+), 10 deletions(-) create mode 100755 .github/run-ghcjs-tests.sh create mode 100644 ghcjs.nix create mode 100644 nix/nixpkgs.nix diff --git a/.github/run-ghcjs-tests.sh b/.github/run-ghcjs-tests.sh new file mode 100755 index 00000000..19e0d9b7 --- /dev/null +++ b/.github/run-ghcjs-tests.sh @@ -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 diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 2eea692c..642b4e30 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -172,8 +172,40 @@ jobs: - name: Run tests run: | - # 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 "$(pkgdir $testpkg)" && nodejs "$testexe".jsexe/all.js); done + + ghcjs-test: + name: ghcjs-test + 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" diff --git a/cabal.ghcjs.project b/cabal.ghcjs.project index 72902332..db1ef33a 100644 --- a/cabal.ghcjs.project +++ b/cabal.ghcjs.project @@ -8,4 +8,6 @@ packages: compiler: ghcjs tests: True -constraints: hashable <=1.3.3.0 +-- Constraints so that reflex-platform provided packages are selected. +constraints: attoparsec == 0.13.2.2 +constraints: hashable == 1.3.0.0 diff --git a/ghcjs.nix b/ghcjs.nix new file mode 100644 index 00000000..274d007c --- /dev/null +++ b/ghcjs.nix @@ -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 + ]; +} diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix new file mode 100644 index 00000000..744f982c --- /dev/null +++ b/nix/nixpkgs.nix @@ -0,0 +1,4 @@ +import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/21.05.tar.gz"; + sha256 = "sha256:1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36"; +}) {} diff --git a/nix/shell.nix b/nix/shell.nix index 715004f6..d178b60e 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -1,10 +1,6 @@ -let nixos = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/refs/tags/21.05.tar.gz"; - sha256 = "sha256:1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36"; -}; in - { compiler ? "ghc8104" , tutorial ? false -, pkgs ? import nixos { config = {}; } +, pkgs ? import ./nixpkgs.nix }: with pkgs; From e9ae1eeed87fe6aa3e521158169ba7f804a33778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Sun, 10 Oct 2021 22:52:13 +0200 Subject: [PATCH 138/156] Remove the old Github action --- .github/workflows/master.yml | 54 +----------------------------------- 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 642b4e30..04b94995 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -123,59 +123,7 @@ jobs: stack test --system-ghc ghcjs: - name: ubuntu-18.04 / ghcjs 8.4 - runs-on: "ubuntu-18.04" - - steps: - - uses: actions/checkout@v2 - - - name: "Setup PATH" - run: | - echo "PATH=$HOME/.cabal/bin:/opt/ghcjs/8.4/bin:$PATH" >> $GITHUB_ENV - - - name: Install ghcjs and cabal - run: | - # Default GitHub image dropped ppa:hvr/ghc, so we add it ourselves - sudo add-apt-repository ppa:hvr/ghc - sudo add-apt-repository ppa:hvr/ghcjs - sudo apt-get update -y - sudo apt-get install ghcjs-8.4 - sudo apt-get install cabal-install - - # Override cabal.project with the lightweight GHCJS one - cp cabal.ghcjs.project cabal.project - cat cabal.project - - - name: Cabal update and freeze - 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: ubuntu-18.04-ghcjs8.4-${{ hashFiles('cabal.project.freeze') }} - restore-keys: | - ubuntu-18.04-ghcjs8.4- - - - name: Install cabal-plan and hspec-discover - run: | - cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project cabal-plan --constraint='cabal-plan ^>=0.6.0.0' --constraint='cabal-plan +exe' - cabal v2-install -w /opt/ghc/8.4.4/bin/ghc --ignore-project hspec-discover - - - name: Build - run: | - cabal v2-build --ghcjs -w /opt/ghcjs/8.4/bin/ghcjs --enable-tests --enable-benchmarks all - - - name: Run tests - run: | - 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 - - ghcjs-test: - name: ghcjs-test + name: ubuntu-latest / ghcjs 8.6 runs-on: "ubuntu-latest" steps: From 51c8edb74d790eb1e914890a64ac4d92d9f04311 Mon Sep 17 00:00:00 2001 From: Richard Marko Date: Mon, 11 Oct 2021 10:29:06 +0200 Subject: [PATCH 139/156] servant-foreign: fix haddock/example typo --- servant-foreign/src/Servant/Foreign/Internal.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-foreign/src/Servant/Foreign/Internal.hs b/servant-foreign/src/Servant/Foreign/Internal.hs index 356f9681..22756db2 100644 --- a/servant-foreign/src/Servant/Foreign/Internal.hs +++ b/servant-foreign/src/Servant/Foreign/Internal.hs @@ -246,7 +246,7 @@ defReq = Req defUrl "GET" [] Nothing Nothing (FunctionName []) ReqBodyJSON -- > -- > -- Or for example in case of lists -- > instance HasForeignType LangX Text a => HasForeignType LangX Text [a] where --- > typeFor lang type _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a) +-- > typeFor lang ftype _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a) -- -- Finally to generate list of information about all the endpoints for -- an API you create a function of a form: From b7c6a9592967da8d8df5804339a755e4125da83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Deest?= Date: Mon, 11 Oct 2021 10:35:40 +0200 Subject: [PATCH 140/156] 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. --- cabal.ghcjs.project | 1 + servant-client-core/servant-client-core.cabal | 2 +- servant-client/servant-client.cabal | 5 +++++ servant/servant.cabal | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cabal.ghcjs.project b/cabal.ghcjs.project index db1ef33a..46d74b2c 100644 --- a/cabal.ghcjs.project +++ b/cabal.ghcjs.project @@ -2,6 +2,7 @@ packages: servant/ + servant-client/ servant-client-core/ -- we need to tell cabal we are using GHCJS diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index b1008f36..e4bc3ba7 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -17,7 +17,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 - , GHCJS == 8.4 + , GHCJS ==8.6.0.1 extra-source-files: CHANGELOG.md diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 8f69a4ad..481dbd0a 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -21,6 +21,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors build-type: Simple tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 + , GHCJS ==8.6.0.1 extra-source-files: CHANGELOG.md @@ -82,6 +83,8 @@ test-suite spec type: exitcode-stdio-1.0 ghc-options: -Wall -rtsopts -threaded "-with-rtsopts=-T -N2" default-language: Haskell2010 + if impl(ghcjs) + buildable: False hs-source-dirs: test main-is: Spec.hs other-modules: @@ -137,3 +140,5 @@ test-suite readme build-tool-depends: markdown-unlit:markdown-unlit ghc-options: -pgmL markdown-unlit default-language: Haskell2010 + if impl(ghcjs) + buildable: False diff --git a/servant/servant.cabal b/servant/servant.cabal index 8dd79c75..7c7af0a1 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -21,7 +21,7 @@ copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant build-type: Simple tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2 || ==9.0.1 - , GHCJS == 8.4 + , GHCJS ==8.6.0.1 extra-source-files: CHANGELOG.md From 43760caf9758ff41f1fd2b80d6533feb2dd12732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20L=C3=A4ll?= Date: Sat, 28 Aug 2021 17:23:41 +0300 Subject: [PATCH 141/156] Fix documentation hierarchy --- doc/tutorial/index.rst | 38 +----------------------------------- doc/tutorial/install.rst | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 37 deletions(-) create mode 100644 doc/tutorial/install.rst diff --git a/doc/tutorial/index.rst b/doc/tutorial/index.rst index c47bfd1d..213a2c6c 100644 --- a/doc/tutorial/index.rst +++ b/doc/tutorial/index.rst @@ -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 `_. -cabal-install --------- - -The whole tutorial is a `cabal `_ -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 `_ 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 `_ 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 diff --git a/doc/tutorial/install.rst b/doc/tutorial/install.rst new file mode 100644 index 00000000..e0d1a70d --- /dev/null +++ b/doc/tutorial/install.rst @@ -0,0 +1,42 @@ +Install +======== + +cabal-install +-------- + +The whole tutorial is a `cabal `_ +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 `_ 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 `_ 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. From f92d2c7ad645d8f184f205817c1139d01fee1d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20L=C3=A4ll?= Date: Tue, 21 Sep 2021 13:48:59 +0300 Subject: [PATCH 142/156] Fix typo --- doc/tutorial/Authentication.lhs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial/Authentication.lhs b/doc/tutorial/Authentication.lhs index 69bb8f10..ded784d4 100644 --- a/doc/tutorial/Authentication.lhs +++ b/doc/tutorial/Authentication.lhs @@ -132,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 From 119e54a800d0943b4752cfbc2c3569d09920750d Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 26 Oct 2021 16:08:30 +0200 Subject: [PATCH 143/156] repatriation of servant-auth in the main servant repo --- cabal.project | 6 + servant-auth/README.md | 1 + servant-auth/RELEASE.md | 7 + servant-auth/servant-auth-client/.ghci | 1 + servant-auth/servant-auth-client/CHANGELOG.md | 26 + servant-auth/servant-auth-client/LICENSE | 31 + servant-auth/servant-auth-client/Setup.hs | 2 + .../servant-auth-client.cabal | 80 +++ .../src/Servant/Auth/Client.hs | 3 + .../src/Servant/Auth/Client/Internal.hs | 64 ++ .../test/Servant/Auth/ClientSpec.hs | 161 +++++ servant-auth/servant-auth-client/test/Spec.hs | 1 + servant-auth/servant-auth-docs/.ghci | 1 + servant-auth/servant-auth-docs/CHANGELOG.md | 14 + servant-auth/servant-auth-docs/LICENSE | 31 + servant-auth/servant-auth-docs/Setup.hs | 33 + .../servant-auth-docs/servant-auth-docs.cabal | 84 +++ .../src/Servant/Auth/Docs.hs | 96 +++ servant-auth/servant-auth-docs/test/Spec.hs | 1 + .../servant-auth-docs/test/doctests.hs | 12 + servant-auth/servant-auth-server/.ghci | 1 + servant-auth/servant-auth-server/CHANGELOG.md | 130 ++++ servant-auth/servant-auth-server/LICENSE | 31 + servant-auth/servant-auth-server/README.lhs | 293 +++++++++ servant-auth/servant-auth-server/README.md | 1 + servant-auth/servant-auth-server/Setup.hs | 2 + .../servant-auth-server.cabal | 129 ++++ .../src/Servant/Auth/Server.hs | 180 ++++++ .../src/Servant/Auth/Server/Internal.hs | 70 ++ .../Auth/Server/Internal/AddSetCookie.hs | 94 +++ .../Servant/Auth/Server/Internal/BasicAuth.hs | 59 ++ .../src/Servant/Auth/Server/Internal/Class.hs | 72 +++ .../Auth/Server/Internal/ConfigTypes.hs | 127 ++++ .../Servant/Auth/Server/Internal/Cookie.hs | 182 ++++++ .../Servant/Auth/Server/Internal/FormLogin.hs | 3 + .../src/Servant/Auth/Server/Internal/JWT.hs | 71 +++ .../Servant/Auth/Server/Internal/ThrowAll.hs | 49 ++ .../src/Servant/Auth/Server/Internal/Types.hs | 112 ++++ .../Servant/Auth/Server/SetCookieOrphan.hs | 3 + .../test/Servant/Auth/ServerSpec.hs | 600 ++++++++++++++++++ servant-auth/servant-auth-server/test/Spec.hs | 1 + servant-auth/servant-auth-swagger/.ghci | 1 + .../servant-auth-swagger/CHANGELOG.md | 24 + servant-auth/servant-auth-swagger/LICENSE | 31 + servant-auth/servant-auth-swagger/Setup.hs | 2 + .../servant-auth-swagger.cabal | 70 ++ .../src/Servant/Auth/Swagger.hs | 87 +++ .../test/Servant/Auth/SwaggerSpec.hs | 38 ++ .../servant-auth-swagger/test/Spec.hs | 1 + servant-auth/servant-auth.project | 6 + servant-auth/servant-auth/.ghci | 1 + servant-auth/servant-auth/CHANGELOG.md | 20 + servant-auth/servant-auth/LICENSE | 31 + servant-auth/servant-auth/Setup.hs | 2 + servant-auth/servant-auth/servant-auth.cabal | 46 ++ servant-auth/servant-auth/src/Servant/Auth.hs | 54 ++ .../servant-auth/src/Servant/Auth/JWT.hs | 33 + servant-auth/servant-auth/test/Spec.hs | 1 + servant-auth/stack-lts16.yaml | 7 + servant-auth/stack-lts17.yaml | 7 + servant-auth/stack.yaml | 7 + 61 files changed, 3334 insertions(+) create mode 120000 servant-auth/README.md create mode 100644 servant-auth/RELEASE.md create mode 100644 servant-auth/servant-auth-client/.ghci create mode 100644 servant-auth/servant-auth-client/CHANGELOG.md create mode 100644 servant-auth/servant-auth-client/LICENSE create mode 100644 servant-auth/servant-auth-client/Setup.hs create mode 100644 servant-auth/servant-auth-client/servant-auth-client.cabal create mode 100644 servant-auth/servant-auth-client/src/Servant/Auth/Client.hs create mode 100644 servant-auth/servant-auth-client/src/Servant/Auth/Client/Internal.hs create mode 100644 servant-auth/servant-auth-client/test/Servant/Auth/ClientSpec.hs create mode 100644 servant-auth/servant-auth-client/test/Spec.hs create mode 100644 servant-auth/servant-auth-docs/.ghci create mode 100644 servant-auth/servant-auth-docs/CHANGELOG.md create mode 100644 servant-auth/servant-auth-docs/LICENSE create mode 100644 servant-auth/servant-auth-docs/Setup.hs create mode 100644 servant-auth/servant-auth-docs/servant-auth-docs.cabal create mode 100644 servant-auth/servant-auth-docs/src/Servant/Auth/Docs.hs create mode 100644 servant-auth/servant-auth-docs/test/Spec.hs create mode 100644 servant-auth/servant-auth-docs/test/doctests.hs create mode 100644 servant-auth/servant-auth-server/.ghci create mode 100644 servant-auth/servant-auth-server/CHANGELOG.md create mode 100644 servant-auth/servant-auth-server/LICENSE create mode 100644 servant-auth/servant-auth-server/README.lhs create mode 120000 servant-auth/servant-auth-server/README.md create mode 100644 servant-auth/servant-auth-server/Setup.hs create mode 100644 servant-auth/servant-auth-server/servant-auth-server.cabal create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/AddSetCookie.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/BasicAuth.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Class.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Cookie.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/FormLogin.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ThrowAll.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Types.hs create mode 100644 servant-auth/servant-auth-server/src/Servant/Auth/Server/SetCookieOrphan.hs create mode 100644 servant-auth/servant-auth-server/test/Servant/Auth/ServerSpec.hs create mode 100644 servant-auth/servant-auth-server/test/Spec.hs create mode 100644 servant-auth/servant-auth-swagger/.ghci create mode 100644 servant-auth/servant-auth-swagger/CHANGELOG.md create mode 100644 servant-auth/servant-auth-swagger/LICENSE create mode 100644 servant-auth/servant-auth-swagger/Setup.hs create mode 100644 servant-auth/servant-auth-swagger/servant-auth-swagger.cabal create mode 100644 servant-auth/servant-auth-swagger/src/Servant/Auth/Swagger.hs create mode 100644 servant-auth/servant-auth-swagger/test/Servant/Auth/SwaggerSpec.hs create mode 100644 servant-auth/servant-auth-swagger/test/Spec.hs create mode 100644 servant-auth/servant-auth.project create mode 100644 servant-auth/servant-auth/.ghci create mode 100644 servant-auth/servant-auth/CHANGELOG.md create mode 100644 servant-auth/servant-auth/LICENSE create mode 100644 servant-auth/servant-auth/Setup.hs create mode 100644 servant-auth/servant-auth/servant-auth.cabal create mode 100644 servant-auth/servant-auth/src/Servant/Auth.hs create mode 100644 servant-auth/servant-auth/src/Servant/Auth/JWT.hs create mode 100644 servant-auth/servant-auth/test/Spec.hs create mode 100644 servant-auth/stack-lts16.yaml create mode 100644 servant-auth/stack-lts17.yaml create mode 100644 servant-auth/stack.yaml diff --git a/cabal.project b/cabal.project index 3e57f34d..e25a87bb 100644 --- a/cabal.project +++ b/cabal.project @@ -1,5 +1,11 @@ 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/ diff --git a/servant-auth/README.md b/servant-auth/README.md new file mode 120000 index 00000000..2cc807b6 --- /dev/null +++ b/servant-auth/README.md @@ -0,0 +1 @@ +servant-auth-server/README.lhs \ No newline at end of file diff --git a/servant-auth/RELEASE.md b/servant-auth/RELEASE.md new file mode 100644 index 00000000..303e4cf3 --- /dev/null +++ b/servant-auth/RELEASE.md @@ -0,0 +1,7 @@ +- update changelog +- bump version in cabal file +- stack sdist servant-auth-server +- git commit -m "v0.4.0.0" +- git tag -s servant-auth-server-0.4.0.0 +- git push --tags +- stack upload servant-auth-server diff --git a/servant-auth/servant-auth-client/.ghci b/servant-auth/servant-auth-client/.ghci new file mode 100644 index 00000000..ae927ec4 --- /dev/null +++ b/servant-auth/servant-auth-client/.ghci @@ -0,0 +1 @@ +:set -isrc -itest -idoctest/ghci-wrapper/src diff --git a/servant-auth/servant-auth-client/CHANGELOG.md b/servant-auth/servant-auth-client/CHANGELOG.md new file mode 100644 index 00000000..2ce9f585 --- /dev/null +++ b/servant-auth/servant-auth-client/CHANGELOG.md @@ -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 diff --git a/servant-auth/servant-auth-client/LICENSE b/servant-auth/servant-auth-client/LICENSE new file mode 100644 index 00000000..302f74f7 --- /dev/null +++ b/servant-auth/servant-auth-client/LICENSE @@ -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. + diff --git a/servant-auth/servant-auth-client/Setup.hs b/servant-auth/servant-auth-client/Setup.hs new file mode 100644 index 00000000..9a994af6 --- /dev/null +++ b/servant-auth/servant-auth-client/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/servant-auth/servant-auth-client/servant-auth-client.cabal b/servant-auth/servant-auth-client/servant-auth-client.cabal new file mode 100644 index 00000000..b385eeb8 --- /dev/null +++ b/servant-auth/servant-auth-client/servant-auth-client.cabal @@ -0,0 +1,80 @@ +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 + + APIs that use + @Auth@ combinator. + . + For a quick overview of the usage, see the . +category: Web, Servant, Authentication +homepage: http://github.com/haskell-servant/servant-auth#readme +bug-reports: https://github.com/haskell-servant/servant-auth/issues +author: Julian K. Arni +maintainer: jkarni@gmail.com +copyright: (c) Julian K. Arni +license: BSD3 +license-file: LICENSE +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 +build-type: Simple +cabal-version: >= 1.10 +extra-source-files: + CHANGELOG.md + +source-repository head + type: git + location: https://github.com/haskell-servant/servant-auth + +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.16 + , bytestring >= 0.10.6.0 && < 0.11 + , containers >= 0.5.6.2 && < 0.7 + , servant-auth == 0.4.* + , servant >= 0.13 && < 0.19 + , servant-client-core >= 0.13 && < 0.19 + + 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.9 + + -- 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.9 + , QuickCheck >= 2.11.3 && < 2.15 + , aeson >= 1.3.1.1 && < 1.6 + , bytestring >= 0.10.6.0 && < 0.11 + , 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.19 + , 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.7.0.0 && < 0.9 + other-modules: + Servant.Auth.ClientSpec + default-language: Haskell2010 diff --git a/servant-auth/servant-auth-client/src/Servant/Auth/Client.hs b/servant-auth/servant-auth-client/src/Servant/Auth/Client.hs new file mode 100644 index 00000000..71e1ad89 --- /dev/null +++ b/servant-auth/servant-auth-client/src/Servant/Auth/Client.hs @@ -0,0 +1,3 @@ +module Servant.Auth.Client (Token(..), Bearer) where + +import Servant.Auth.Client.Internal (Bearer, Token(..)) diff --git a/servant-auth/servant-auth-client/src/Servant/Auth/Client/Internal.hs b/servant-auth/servant-auth-client/src/Servant/Auth/Client/Internal.hs new file mode 100644 index 00000000..4cdc9dd9 --- /dev/null +++ b/servant-auth/servant-auth-client/src/Servant/Auth/Client/Internal.hs @@ -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 @ +-- +-- 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 diff --git a/servant-auth/servant-auth-client/test/Servant/Auth/ClientSpec.hs b/servant-auth/servant-auth-client/test/Servant/Auth/ClientSpec.hs new file mode 100644 index 00000000..fdd22ab2 --- /dev/null +++ b/servant-auth/servant-auth-client/test/Servant/Auth/ClientSpec.hs @@ -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 + +-- }}} diff --git a/servant-auth/servant-auth-client/test/Spec.hs b/servant-auth/servant-auth-client/test/Spec.hs new file mode 100644 index 00000000..a824f8c3 --- /dev/null +++ b/servant-auth/servant-auth-client/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/servant-auth/servant-auth-docs/.ghci b/servant-auth/servant-auth-docs/.ghci new file mode 100644 index 00000000..ae927ec4 --- /dev/null +++ b/servant-auth/servant-auth-docs/.ghci @@ -0,0 +1 @@ +:set -isrc -itest -idoctest/ghci-wrapper/src diff --git a/servant-auth/servant-auth-docs/CHANGELOG.md b/servant-auth/servant-auth-docs/CHANGELOG.md new file mode 100644 index 00000000..0a255fb1 --- /dev/null +++ b/servant-auth/servant-auth-docs/CHANGELOG.md @@ -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 diff --git a/servant-auth/servant-auth-docs/LICENSE b/servant-auth/servant-auth-docs/LICENSE new file mode 100644 index 00000000..302f74f7 --- /dev/null +++ b/servant-auth/servant-auth-docs/LICENSE @@ -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. + diff --git a/servant-auth/servant-auth-docs/Setup.hs b/servant-auth/servant-auth-docs/Setup.hs new file mode 100644 index 00000000..8ec54a08 --- /dev/null +++ b/servant-auth/servant-auth-docs/Setup.hs @@ -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 diff --git a/servant-auth/servant-auth-docs/servant-auth-docs.cabal b/servant-auth/servant-auth-docs/servant-auth-docs.cabal new file mode 100644 index 00000000..f00cc575 --- /dev/null +++ b/servant-auth/servant-auth-docs/servant-auth-docs.cabal @@ -0,0 +1,84 @@ +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 + + APIs that use + @Auth@ combinator. + . + For a quick overview of the usage, see the . +category: Web, Servant, Authentication +homepage: http://github.com/haskell-servant/servant-auth#readme +bug-reports: https://github.com/haskell-servant/servant-auth/issues +author: Julian K. Arni +maintainer: jkarni@gmail.com +copyright: (c) Julian K. Arni +license: BSD3 +license-file: LICENSE +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 +build-type: Custom +cabal-version: >= 1.10 +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-auth + +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.16 + , servant-docs >= 0.11.2 && < 0.12 + , servant >= 0.13 && < 0.19 + , servant-auth == 0.4.* + , lens >= 4.16.1 && <5.1 + 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.19, + 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.9 + + -- 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.9 + , QuickCheck >= 2.11.3 && < 2.15 + + default-language: Haskell2010 diff --git a/servant-auth/servant-auth-docs/src/Servant/Auth/Docs.hs b/servant-auth/servant-auth-docs/src/Servant/Auth/Docs.hs new file mode 100644 index 00000000..da507990 --- /dev/null +++ b/servant-auth/servant-auth-docs/src/Servant/Auth/Docs.hs @@ -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 diff --git a/servant-auth/servant-auth-docs/test/Spec.hs b/servant-auth/servant-auth-docs/test/Spec.hs new file mode 100644 index 00000000..a824f8c3 --- /dev/null +++ b/servant-auth/servant-auth-docs/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/servant-auth/servant-auth-docs/test/doctests.hs b/servant-auth/servant-auth-docs/test/doctests.hs new file mode 100644 index 00000000..aff961f5 --- /dev/null +++ b/servant-auth/servant-auth-docs/test/doctests.hs @@ -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 diff --git a/servant-auth/servant-auth-server/.ghci b/servant-auth/servant-auth-server/.ghci new file mode 100644 index 00000000..ae927ec4 --- /dev/null +++ b/servant-auth/servant-auth-server/.ghci @@ -0,0 +1 @@ +:set -isrc -itest -idoctest/ghci-wrapper/src diff --git a/servant-auth/servant-auth-server/CHANGELOG.md b/servant-auth/servant-auth-server/CHANGELOG.md new file mode 100644 index 00000000..34b137d2 --- /dev/null +++ b/servant-auth/servant-auth-server/CHANGELOG.md @@ -0,0 +1,130 @@ +# 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.6.0] - 2020-10-06 + +## Changed + +- expose verifyJWT and use it in two places [@domenkozar] +- support GHC 8.10 [@domenkozar] +- move ToJWT/FromJWT to servant-auth [@erewok] +- #165 fix AnySite with Cookie 3.5.0 [@odr] + +## [0.4.5.1] - 2020-02-06 + +## Changed + +- #158 servant 0.17 support [@phadej] + +## [0.4.5.0] - 2019-12-28 + +## Changed +- #144 servant 0.16 support and drop GHC 7.10 support [@domenkozar] +- #148 removed unused constaint in HasServer instance for Auth +- #154 GHC 8.8 support [@phadej] + +### Added +- #141 Support Stream combinator [@domenkozar] +- #143 Allow servant-0.16 [@phadej] + +## [0.4.4.0] - 2019-03-02 + +### Added +- #141 Support Stream combinator [@domenkozar] +- #143 Allow servant-0.16 [@phadej] + +## [0.4.3.0] - 2019-01-17 + +## Changed +- #117 Avoid running auth checks unnecessarily [@sopvop] +- #110 Get rid of crypto-api dependency [@domenkozar] +- #130 clearSession: improve cross-browser compatibility [@domenkozar] +- #136 weed out bytestring-conversion [@stephenirl] + +## [0.4.2.0] - 2018-11-05 + +### Added +- `Headers hs a` instance for AddSetCookieApi [@domenkozar] +- GHC 8.6.x support [@domenkozar] + +## [0.4.1.0] - 2018-10-05 + +### Added +- #125 Allow setting domain name for a cookie [@domenkozar] + +## Changed +- bump http-api-data to 0.3.10 that includes Cookie orphan instances previously located in servant-auth-server [@phadej] +- #114 Export `HasSecurity` typeclass [@rockbmb] + +## [0.4.0.1] - 2018-09-23 + +### Security +- #123 Session cookie did not apply SameSite attribute [@domenkozar] + +### Added +- #112 HasLink instance for Auth combinator [@adetokunbo] +- #111 Documentation for using hoistServer [@mschristiansen] +- #107 Add utility functions for reading and writing a key to a file [@mschristiansen] + +## [0.4.0.0] - 2018-06-17 + +### Added +- Support GHC 8.4 by @phadej and @domenkozar +- Support for servant-0.14 by @phadej +- #96 Support for jose-0.7 by @xaviershay +- #92 add `clearSession` for logout by @plredmond and @3noch +- #95 makeJWT: allow setting Alg via defaultJWTSettings by @domenkozar +- #89 Validate JWT against a JWKSet instead of JWK by @sopvop + +### Changed +- #92 Rename CSRF to XSRF by @plredmond and @3noch +- #92 extract 'XsrfCookieSettings' from 'CookieSettings' and make XSRF checking optional + by @plredmond and @3noch +- #69 export SameSite by @domenkozar +- #102 Reuse Servant.Api.IsSecure instead of duplicating ADT by @domenkozar + +### Deprecated +- #92 Renamed 'makeCsrfCookie' to 'makeXsrfCookie' and marked the former as deprecated + by @plredmond and @3noc +- #92 Made several changes to the structure of 'CookieSettings' which will require + attention by users who have modified the XSRF settings by @plredmond and @3noch + +### Security +- #94 Force cookie expiration on serverside by @karshan + +## [0.3.2.0] - 2018-02-21 + +### Added +- #76 Export wwwAuthenticatedErr and elaborate its annotation by @defanor +- Support for servant-0.14 by @phadej + +### Changed +- Disable the readme executable for ghcjs builds by @hamishmack +- #84 Make AddSetCookieApi type family open by @qnikst +- #79 Make CSRF checks optional for GET requests by @harendra-kumar + +## [0.3.1.0] - 2017-11-08 + +### Added +- Support for servant-0.12 by @phadej + +## [0.3.0.0] - 2017-11-07 + +### Changed +- #47 'cookiePath' and 'xsrfCookiePath' added to 'CookieSettings' by @mchaver + +## [0.2.8.0] - 2017-05-26 + +### Added +- #45 Support for servant-0.11 by @phadej + +## [0.2.7.0] - 2017-02-11 + +### Changed +- #27 #41 'acceptLogin' and 'makeCsrfCookie' functions by @bts diff --git a/servant-auth/servant-auth-server/LICENSE b/servant-auth/servant-auth-server/LICENSE new file mode 100644 index 00000000..302f74f7 --- /dev/null +++ b/servant-auth/servant-auth-server/LICENSE @@ -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. + diff --git a/servant-auth/servant-auth-server/README.lhs b/servant-auth/servant-auth-server/README.lhs new file mode 100644 index 00000000..9dba4acc --- /dev/null +++ b/servant-auth/servant-auth-server/README.lhs @@ -0,0 +1,293 @@ +# servant-auth + +[![build status](https://img.shields.io/github/workflow/status/haskell-servant/servant-auth/CI/master?style=flat-square&logo=github&label=build%20status)](https://github.com/haskell-servant/servant-auth/actions?query=workflow%3ACI) + +These packages provides safe and easy-to-use authentication options for +`servant`. The same API can be protected via: +- basicauth +- cookies +- JWT tokens + + +| Package | Hackage | +| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| servant-auth | [![servant-auth](https://img.shields.io/hackage/v/servant-auth?style=flat-square&logo=haskell&label&labelColor=5D4F85)](https://hackage.haskell.org/package/servant-auth) | +| servant-auth-server | [![servant-auth-server](https://img.shields.io/hackage/v/servant-auth-server.svg?style=flat-square&logo=haskell&label&labelColor=5D4F85)](https://hackage.haskell.org/package/servant-auth-server) | +| servant-auth-client | [![servant-auth-client](https://img.shields.io/hackage/v/servant-auth-client.svg?style=flat-square&logo=haskell&label&labelColor=5D4F85)](https://hackage.haskell.org/package/servant-auth-client) | +| servant-auth-swagger | [![servant-auth-swagger](https://img.shields.io/hackage/v/servant-auth-swagger.svg?style=flat-square&logo=haskell&label&labelColor=5D4F85)](https://hackage.haskell.org/package/servant-auth-swagger) | +| servant-auth-docs | [![servant-auth-docs](https://img.shields.io/hackage/v/servant-auth-docs.svg?style=flat-square&logo=haskell&label&labelColor=5D4F85)](https://hackage.haskell.org/package/servant-auth-docs) | + +## How it works + +First some imports: + +~~~ haskell +{-# OPTIONS_GHC -fno-warn-unused-binds #-} +{-# OPTIONS_GHC -fno-warn-deprecations #-} +import Control.Concurrent (forkIO) +import Control.Monad (forever) +import Control.Monad.Trans (liftIO) +import Data.Aeson (FromJSON, ToJSON) +import GHC.Generics (Generic) +import Network.Wai.Handler.Warp (run) +import System.Environment (getArgs) +import Servant +import Servant.Auth.Server +import Servant.Auth.Server.SetCookieOrphan () +~~~ + +`servant-auth` library introduces a combinator `Auth`: + +~~~ haskell +data Auth (auths :: [*]) val +~~~ + +What `Auth [Auth1, Auth2] Something :> API` means is that `API` is protected by +*either* `Auth1` *or* `Auth2`, and the result of authentication will be of type +`AuthResult Something`, where : + +~~~ haskell +data AuthResult val + = BadPassword + | NoSuchUser + | Authenticated val + | Indefinite +~~~ + +Your handlers will get a value of type `AuthResult Something`, and can decide +what to do with it. + +~~~ haskell + +data User = User { name :: String, email :: String } + deriving (Eq, Show, Read, Generic) + +instance ToJSON User +instance ToJWT User +instance FromJSON User +instance FromJWT User + +data Login = Login { username :: String, password :: String } + deriving (Eq, Show, Read, Generic) + +instance ToJSON Login +instance FromJSON Login + +type Protected + = "name" :> Get '[JSON] String + :<|> "email" :> Get '[JSON] String + + +-- | 'Protected' will be protected by 'auths', which we still have to specify. +protected :: Servant.Auth.Server.AuthResult User -> Server Protected +-- If we get an "Authenticated v", we can trust the information in v, since +-- it was signed by a key we trust. +protected (Servant.Auth.Server.Authenticated user) = return (name user) :<|> return (email user) +-- Otherwise, we return a 401. +protected _ = throwAll err401 + +type Unprotected = + "login" + :> ReqBody '[JSON] Login + :> Verb 'POST 204 '[JSON] (Headers '[ Header "Set-Cookie" SetCookie + , Header "Set-Cookie" SetCookie] + NoContent) + :<|> Raw + +unprotected :: CookieSettings -> JWTSettings -> Server Unprotected +unprotected cs jwts = checkCreds cs jwts :<|> serveDirectory "example/static" + +type API auths = (Servant.Auth.Server.Auth auths User :> Protected) :<|> Unprotected + +server :: CookieSettings -> JWTSettings -> Server (API auths) +server cs jwts = protected :<|> unprotected cs jwts + +~~~ + +The code is common to all authentications. In order to pick one or more specific +authentication methods, all we need to do is provide the expect configuration +parameters. + +## API tokens + +The following example illustrates how to protect an API with tokens. + + +~~~ haskell +-- In main, we fork the server, and allow new tokens to be created in the +-- command line for the specified user name and email. +mainWithJWT :: IO () +mainWithJWT = do + -- We generate the key for signing tokens. This would generally be persisted, + -- and kept safely + myKey <- generateKey + -- Adding some configurations. All authentications require CookieSettings to + -- be in the context. + let jwtCfg = defaultJWTSettings myKey + cfg = defaultCookieSettings :. jwtCfg :. EmptyContext + --- Here we actually make concrete + api = Proxy :: Proxy (API '[JWT]) + _ <- forkIO $ run 7249 $ serveWithContext api cfg (server defaultCookieSettings jwtCfg) + + putStrLn "Started server on localhost:7249" + putStrLn "Enter name and email separated by a space for a new token" + + forever $ do + xs <- words <$> getLine + case xs of + [name', email'] -> do + etoken <- makeJWT (User name' email') jwtCfg Nothing + case etoken of + Left e -> putStrLn $ "Error generating token:t" ++ show e + Right v -> putStrLn $ "New token:\t" ++ show v + _ -> putStrLn "Expecting a name and email separated by spaces" + +~~~ + +And indeed: + +~~~ bash + +./readme JWT + + Started server on localhost:7249 + Enter name and email separated by a space for a new token + alice alice@gmail.com + New token: "eyJhbGciOiJIUzI1NiJ9.eyJkYXQiOnsiZW1haWwiOiJhbGljZUBnbWFpbC5jb20iLCJuYW1lIjoiYWxpY2UifX0.xzOIrx_A9VOKzVO-R1c1JYKBqK9risF625HOxpBzpzE" + +curl localhost:7249/name -v + + * Hostname was NOT found in DNS cache + * Trying 127.0.0.1... + * Connected to localhost (127.0.0.1) port 7249 (#0) + > GET /name HTTP/1.1 + > User-Agent: curl/7.35.0 + > Host: localhost:7249 + > Accept: */* + > + < HTTP/1.1 401 Unauthorized + < Transfer-Encoding: chunked + < Date: Wed, 07 Sep 2016 20:17:17 GMT + * Server Warp/3.2.7 is not blacklisted + < Server: Warp/3.2.7 + < + * Connection #0 to host localhost left intact + +curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJkYXQiOnsiZW1haWwiOiJhbGljZUBnbWFpbC5jb20iLCJuYW1lIjoiYWxpY2UifX0.xzOIrx_A9VOKzVO-R1c1JYKBqK9risF625HOxpBzpzE" \ + localhost:7249/name -v + + * Hostname was NOT found in DNS cache + * Trying 127.0.0.1... + * Connected to localhost (127.0.0.1) port 7249 (#0) + > GET /name HTTP/1.1 + > User-Agent: curl/7.35.0 + > Host: localhost:7249 + > Accept: */* + > Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJkYXQiOnsiZW1haWwiOiJhbGljZUBnbWFpbC5jb20iLCJuYW1lIjoiYWxpY2UifX0.xzOIrx_A9VOKzVO-R1c1JYKBqK9risF625HOxpBzpzE + > + < HTTP/1.1 200 OK + < Transfer-Encoding: chunked + < Date: Wed, 07 Sep 2016 20:16:11 GMT + * Server Warp/3.2.7 is not blacklisted + < Server: Warp/3.2.7 + < Content-Type: application/json + < Set-Cookie: JWT-Cookie=eyJhbGciOiJIUzI1NiJ9.eyJkYXQiOnsiZW1haWwiOiJhbGljZUBnbWFpbC5jb20iLCJuYW1lIjoiYWxpY2UifX0.xzOIrx_A9VOKzVO-R1c1JYKBqK9risF625HOxpBzpzE; HttpOnly; Secure + < Set-Cookie: XSRF-TOKEN=TWcdPnHr2QHcVyTw/TTBLQ==; Secure + < + * Connection #0 to host localhost left intact + "alice"% + + +~~~ + +## Cookies + +What if, in addition to API tokens, we want to expose our API to browsers? All +we need to do is say so! + +~~~ haskell +mainWithCookies :: IO () +mainWithCookies = do + -- We *also* need a key to sign the cookies + myKey <- generateKey + -- Adding some configurations. 'Cookie' requires, in addition to + -- CookieSettings, JWTSettings (for signing), so everything is just as before + let jwtCfg = defaultJWTSettings myKey + cfg = defaultCookieSettings :. jwtCfg :. EmptyContext + --- Here is the actual change + api = Proxy :: Proxy (API '[Cookie]) + run 7249 $ serveWithContext api cfg (server defaultCookieSettings jwtCfg) + +-- Here is the login handler +checkCreds :: CookieSettings + -> JWTSettings + -> Login + -> Handler (Headers '[ Header "Set-Cookie" SetCookie + , Header "Set-Cookie" SetCookie] + NoContent) +checkCreds cookieSettings jwtSettings (Login "Ali Baba" "Open Sesame") = do + -- Usually you would ask a database for the user info. This is just a + -- regular servant handler, so you can follow your normal database access + -- patterns (including using 'enter'). + let usr = User "Ali Baba" "ali@email.com" + mApplyCookies <- liftIO $ acceptLogin cookieSettings jwtSettings usr + case mApplyCookies of + Nothing -> throwError err401 + Just applyCookies -> return $ applyCookies NoContent +checkCreds _ _ _ = throwError err401 +~~~ + +### XSRF and the frontend + +XSRF protection works by requiring that there be a header of the same value as +a distinguished cookie that is set by the server on each request. What the +cookie and header name are can be configured (see `xsrfCookieName` and +`xsrfHeaderName` in `CookieSettings`), but by default they are "XSRF-TOKEN" and +"X-XSRF-TOKEN". This means that, if your client is a browser and you're using +cookies, Javascript on the client must set the header of each request by +reading the cookie. For jQuery, and with the default values, that might be: + +~~~ javascript + +var token = (function() { + r = document.cookie.match(new RegExp('XSRF-TOKEN=([^;]+)')) + if (r) return r[1]; +})(); + + +$.ajaxPrefilter(function(opts, origOpts, xhr) { + xhr.setRequestHeader('X-XSRF-TOKEN', token); + } + +~~~ + +I *believe* nothing at all needs to be done if you're using Angular's `$http` +directive, but I haven't tested this. + +XSRF protection can be disabled just for `GET` requests by setting +`xsrfExcludeGet = False`. You might want this if you're relying on the browser +to navigate between pages that require cookie authentication. + +XSRF protection can be completely disabled by setting `cookieXsrfSetting = +Nothing` in `CookieSettings`. This is not recommended! If your cookie +authenticated web application runs any javascript, it's recommended to send the +XSRF header. However, if your web application runs no javascript, disabling +XSRF entirely may be required. + +# Note on this README + +This README is a literate haskell file. Here is 'main', allowing you to pick +between the examples above. + +~~~ haskell + +main :: IO () +main = do + args <- getArgs + let usage = "Usage: readme (JWT|Cookie)" + case args of + ["JWT"] -> mainWithJWT + ["Cookie"] -> mainWithCookies + e -> putStrLn $ "Arguments: \"" ++ unwords e ++ "\" not understood\n" ++ usage + +~~~ diff --git a/servant-auth/servant-auth-server/README.md b/servant-auth/servant-auth-server/README.md new file mode 120000 index 00000000..4e381b2e --- /dev/null +++ b/servant-auth/servant-auth-server/README.md @@ -0,0 +1 @@ +README.lhs \ No newline at end of file diff --git a/servant-auth/servant-auth-server/Setup.hs b/servant-auth/servant-auth-server/Setup.hs new file mode 100644 index 00000000..9a994af6 --- /dev/null +++ b/servant-auth/servant-auth-server/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/servant-auth/servant-auth-server/servant-auth-server.cabal b/servant-auth/servant-auth-server/servant-auth-server.cabal new file mode 100644 index 00000000..a657e97d --- /dev/null +++ b/servant-auth/servant-auth-server/servant-auth-server.cabal @@ -0,0 +1,129 @@ +name: servant-auth-server +version: 0.4.6.0 +synopsis: servant-server/servant-auth compatibility +description: This package provides the required instances for using the @Auth@ combinator + in your 'servant' server. + . + Both cookie- and token- (REST API) based authentication is provided. + . + For a quick overview of the usage, see the . +category: Web, Servant, Authentication +homepage: http://github.com/haskell-servant/servant-auth#readme +bug-reports: https://github.com/haskell-servant/servant-auth/issues +author: Julian K. Arni +maintainer: jkarni@gmail.com +copyright: (c) Julian K. Arni +license: BSD3 +license-file: LICENSE +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 +build-type: Simple +cabal-version: >= 1.10 +extra-source-files: + CHANGELOG.md + +source-repository head + type: git + location: https://github.com/haskell-servant/servant-auth + +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.16 + , aeson >= 1.3.1.1 && < 1.6 + , base64-bytestring >= 1.0.0.1 && < 1.2 + , blaze-builder >= 0.4.1.0 && < 0.5 + , bytestring >= 0.10.6.0 && < 0.11 + , case-insensitive >= 1.2.0.11 && < 1.3 + , cookie >= 0.4.4 && < 0.5 + , data-default-class >= 0.1.2.0 && < 0.2 + , entropy >= 0.4.1.3 && < 0.5 + , http-types >= 0.12.2 && < 0.13 + , jose >= 0.7.0.0 && < 0.9 + , lens >= 4.16.1 && < 5.1 + , memory >= 0.14.16 && < 0.17 + , monad-time >= 0.3.1.0 && < 0.4 + , mtl >= 2.2.2 && < 2.3 + , servant >= 0.13 && < 0.19 + , servant-auth == 0.4.* + , servant-server >= 0.13 && < 0.19 + , tagged >= 0.8.4 && < 0.9 + , text >= 1.2.3.0 && < 1.3 + , time >= 1.5.0.1 && < 1.10 + , unordered-containers >= 0.2.9.0 && < 0.3 + , wai >= 3.2.1.2 && < 3.3 + if !impl(ghc >= 8.0) + build-depends: + semigroups >= 0.18.5 && <0.20 + exposed-modules: + Servant.Auth.Server + Servant.Auth.Server.Internal + Servant.Auth.Server.Internal.AddSetCookie + Servant.Auth.Server.Internal.BasicAuth + Servant.Auth.Server.Internal.Class + Servant.Auth.Server.Internal.ConfigTypes + Servant.Auth.Server.Internal.Cookie + Servant.Auth.Server.Internal.FormLogin + Servant.Auth.Server.Internal.JWT + Servant.Auth.Server.Internal.ThrowAll + Servant.Auth.Server.Internal.Types + Servant.Auth.Server.SetCookieOrphan + default-language: Haskell2010 + +test-suite readme + type: exitcode-stdio-1.0 + main-is: README.lhs + default-extensions: ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators + ghc-options: -Wall -pgmL markdown-unlit + build-tool-depends: markdown-unlit:markdown-unlit + build-depends: + base + , servant-auth + , servant-auth-server + , servant-server + , aeson + , mtl + , warp + default-language: Haskell2010 + if impl(ghcjs) + buildable: False + +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.8 + + -- dependencies with bounds inherited from the library stanza + build-depends: + base + , aeson + , bytestring + , case-insensitive + , jose + , lens + , mtl + , time + , http-types + , wai + , servant + , servant-server + , transformers + + -- test dependencies + build-depends: + servant-auth-server + , hspec >= 2.5.5 && < 2.8 + , QuickCheck >= 2.11.3 && < 2.15 + , http-client >= 0.5.13.1 && < 0.8 + , lens-aeson >= 1.0.2 && < 1.2 + , warp >= 3.2.25 && < 3.4 + , wreq >= 0.5.2.1 && < 0.6 + other-modules: + Servant.Auth.ServerSpec + default-language: Haskell2010 diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server.hs new file mode 100644 index 00000000..d163fc26 --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server.hs @@ -0,0 +1,180 @@ +module Servant.Auth.Server + ( + -- | This package provides implementations for some common authentication + -- methods. Authentication yields a trustworthy (because generated by the + -- server) value of an some arbitrary type: + -- + -- > type MyApi = Protected + -- > + -- > type Protected = Auth '[JWT, Cookie] User :> Get '[JSON] UserAccountDetails + -- > + -- > server :: Server Protected + -- > server (Authenticated usr) = ... -- here we know the client really is + -- > -- who she claims to be + -- > server _ = throwAll err401 + -- + -- Additional configuration happens via 'Context'. + -- + -- == Example for Custom Handler + -- To use a custom 'Servant.Server.Handler' it is necessary to use + -- 'Servant.Server.hoistServerWithContext' instead of + -- 'Servant.Server.hoistServer' and specify the 'Context'. + -- + -- Below is an example of passing 'CookieSettings' and 'JWTSettings' in the + -- 'Context' to create a specialized function equivalent to + -- 'Servant.Server.hoistServer' for an API that includes cookie + -- authentication. + -- + -- > hoistServerWithAuth + -- > :: HasServer api '[CookieSettings, JWTSettings] + -- > => Proxy api + -- > -> (forall x. m x -> n x) + -- > -> ServerT api m + -- > -> ServerT api n + -- > hoistServerWithAuth api = + -- > hoistServerWithContext api (Proxy :: Proxy '[CookieSettings, JWTSettings]) + + ---------------------------------------------------------------------------- + -- * Auth + -- | Basic types + Auth + , AuthResult(..) + , AuthCheck(..) + + ---------------------------------------------------------------------------- + -- * JWT + -- | JSON Web Tokens (JWT) are a compact and secure way of transferring + -- information between parties. In this library, they are signed by the + -- server (or by some other party posessing the relevant key), and used to + -- indicate the bearer's identity or authorization. + -- + -- Arbitrary information can be encoded - just declare instances for the + -- 'FromJWT' and 'ToJWT' classes. Don't go overboard though - be aware that + -- usually you'll be trasmitting this information on each request (and + -- response!). + -- + -- Note that, while the tokens are signed, they are not encrypted. Do not put + -- any information you do not wish the client to know in them! + + -- ** Combinator + -- | Re-exported from 'servant-auth' + , JWT + + -- ** Classes + , FromJWT(..) + , ToJWT(..) + + -- ** Related types + , IsMatch(..) + + -- ** Settings + , JWTSettings(..) + , defaultJWTSettings + + -- ** Create check + , jwtAuthCheck + + + ---------------------------------------------------------------------------- + -- * Cookie + -- | Cookies are also a method of identifying and authenticating a user. They + -- are particular common when the client is a browser + + -- ** Combinator + -- | Re-exported from 'servant-auth' + , Cookie + + -- ** Settings + , CookieSettings(..) + , XsrfCookieSettings(..) + , defaultCookieSettings + , defaultXsrfCookieSettings + , makeSessionCookie + , makeSessionCookieBS + , makeXsrfCookie + , makeCsrfCookie + , makeCookie + , makeCookieBS + , acceptLogin + , clearSession + + + -- ** Related types + , IsSecure(..) + , SameSite(..) + , AreAuths + + ---------------------------------------------------------------------------- + -- * BasicAuth + -- ** Combinator + -- | Re-exported from 'servant-auth' + , BasicAuth + + -- ** Classes + , FromBasicAuthData(..) + + -- ** Settings + , BasicAuthCfg + + -- ** Related types + , BasicAuthData(..) + , IsPasswordCorrect(..) + + -- ** Authentication request + , wwwAuthenticatedErr + + ---------------------------------------------------------------------------- + -- * Utilies + , ThrowAll(throwAll) + , generateKey + , generateSecret + , fromSecret + , writeKey + , readKey + , makeJWT + , verifyJWT + + -- ** Re-exports + , Default(def) + , SetCookie + ) where + +import Prelude hiding (readFile, writeFile) +import Data.ByteString (ByteString, writeFile, readFile) +import Data.Default.Class (Default (def)) +import Servant.Auth +import Servant.Auth.JWT +import Servant.Auth.Server.Internal () +import Servant.Auth.Server.Internal.BasicAuth +import Servant.Auth.Server.Internal.Class +import Servant.Auth.Server.Internal.ConfigTypes +import Servant.Auth.Server.Internal.Cookie +import Servant.Auth.Server.Internal.JWT +import Servant.Auth.Server.Internal.ThrowAll +import Servant.Auth.Server.Internal.Types + +import Crypto.JOSE as Jose +import Servant (BasicAuthData (..)) +import Web.Cookie (SetCookie) + +-- | Generate a key suitable for use with 'defaultConfig'. +generateKey :: IO Jose.JWK +generateKey = Jose.genJWK $ Jose.OctGenParam 256 + +-- | Generate a bytestring suitable for use with 'fromSecret'. +generateSecret :: MonadRandom m => m ByteString +generateSecret = Jose.getRandomBytes 256 + +-- | Restores a key from a bytestring. +fromSecret :: ByteString -> Jose.JWK +fromSecret = Jose.fromOctets + +-- | Writes a secret to a file. Can for instance be used from the REPL +-- to persist a key to a file, which can then be included with the +-- application. Restore the key using 'readKey'. +writeKey :: FilePath -> IO () +writeKey fp = writeFile fp =<< generateSecret + +-- | Reads a key from a file. +readKey :: FilePath -> IO Jose.JWK +readKey fp = fromSecret <$> readFile fp diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal.hs new file mode 100644 index 00000000..2e825c0a --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Servant.Auth.Server.Internal where + +import Control.Monad.Trans (liftIO) +import Servant ((:>), Handler, HasServer (..), + Proxy (..), + HasContextEntry(getContextEntry)) +import Servant.Auth +import Servant.Auth.JWT (ToJWT) + +import Servant.Auth.Server.Internal.AddSetCookie +import Servant.Auth.Server.Internal.Class +import Servant.Auth.Server.Internal.Cookie +import Servant.Auth.Server.Internal.ConfigTypes +import Servant.Auth.Server.Internal.JWT +import Servant.Auth.Server.Internal.Types + +import Servant.Server.Internal (DelayedIO, addAuthCheck, withRequest) + +instance ( n ~ 'S ('S 'Z) + , HasServer (AddSetCookiesApi n api) ctxs, AreAuths auths ctxs v + , HasServer api ctxs -- this constraint is needed to implement hoistServer + , AddSetCookies n (ServerT api Handler) (ServerT (AddSetCookiesApi n api) Handler) + , ToJWT v + , HasContextEntry ctxs CookieSettings + , HasContextEntry ctxs JWTSettings + ) => HasServer (Auth auths v :> api) ctxs where + type ServerT (Auth auths v :> api) m = AuthResult v -> ServerT api m + +#if MIN_VERSION_servant_server(0,12,0) + hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s +#endif + + route _ context subserver = + route (Proxy :: Proxy (AddSetCookiesApi n api)) + context + (fmap go subserver `addAuthCheck` authCheck) + + where + authCheck :: DelayedIO (AuthResult v, SetCookieList ('S ('S 'Z))) + authCheck = withRequest $ \req -> liftIO $ do + authResult <- runAuthCheck (runAuths (Proxy :: Proxy auths) context) req + cookies <- makeCookies authResult + return (authResult, cookies) + + jwtSettings :: JWTSettings + jwtSettings = getContextEntry context + + cookieSettings :: CookieSettings + cookieSettings = getContextEntry context + + makeCookies :: AuthResult v -> IO (SetCookieList ('S ('S 'Z))) + makeCookies authResult = do + xsrf <- makeXsrfCookie cookieSettings + fmap (Just xsrf `SetCookieCons`) $ + case authResult of + (Authenticated v) -> do + ejwt <- makeSessionCookie cookieSettings jwtSettings v + case ejwt of + Nothing -> return $ Nothing `SetCookieCons` SetCookieNil + Just jwt -> return $ Just jwt `SetCookieCons` SetCookieNil + _ -> return $ Nothing `SetCookieCons` SetCookieNil + + go :: (AuthResult v -> ServerT api Handler) + -> (AuthResult v, SetCookieList n) + -> ServerT (AddSetCookiesApi n api) Handler + go fn (authResult, cookies) = addSetCookies cookies $ fn authResult diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/AddSetCookie.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/AddSetCookie.hs new file mode 100644 index 00000000..32857ebe --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/AddSetCookie.hs @@ -0,0 +1,94 @@ +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE CPP #-} + +module Servant.Auth.Server.Internal.AddSetCookie where + +import Blaze.ByteString.Builder (toByteString) +import qualified Data.ByteString as BS +import Data.Tagged (Tagged (..)) +import qualified Network.HTTP.Types as HTTP +import Network.Wai (mapResponseHeaders) +import Servant +import Web.Cookie + +-- What are we doing here? Well, the idea is to add headers to the response, +-- but the headers come from the authentication check. In order to do that, we +-- tweak a little the general theme of recursing down the API tree; this time, +-- we recurse down a variation of it that adds headers to all the endpoints. +-- This involves the usual type-level checks. +-- +-- TODO: If the endpoints already have headers, this will not work as is. + +data Nat = Z | S Nat + +type family AddSetCookiesApi (n :: Nat) a where + AddSetCookiesApi ('S 'Z) a = AddSetCookieApi a + AddSetCookiesApi ('S n) a = AddSetCookiesApi n (AddSetCookieApi a) + +type family AddSetCookieApiVerb a where + AddSetCookieApiVerb (Headers ls a) = Headers (Header "Set-Cookie" SetCookie ': ls) a + AddSetCookieApiVerb a = Headers '[Header "Set-Cookie" SetCookie] a + +type family AddSetCookieApi a :: * +type instance AddSetCookieApi (a :> b) = a :> AddSetCookieApi b +type instance AddSetCookieApi (a :<|> b) = AddSetCookieApi a :<|> AddSetCookieApi b +type instance AddSetCookieApi (Verb method stat ctyps a) + = Verb method stat ctyps (AddSetCookieApiVerb a) +type instance AddSetCookieApi Raw = Raw +#if MIN_VERSION_servant_server(0,15,0) +type instance AddSetCookieApi (Stream method stat framing ctyps a) + = Stream method stat framing ctyps (AddSetCookieApiVerb a) +#endif +type instance AddSetCookieApi (Headers hs a) = AddSetCookieApiVerb (Headers hs a) + +data SetCookieList (n :: Nat) :: * where + SetCookieNil :: SetCookieList 'Z + SetCookieCons :: Maybe SetCookie -> SetCookieList n -> SetCookieList ('S n) + +class AddSetCookies (n :: Nat) orig new where + addSetCookies :: SetCookieList n -> orig -> new + +instance {-# OVERLAPS #-} AddSetCookies ('S n) oldb newb + => AddSetCookies ('S n) (a -> oldb) (a -> newb) where + addSetCookies cookies oldfn = addSetCookies cookies . oldfn + +instance AddSetCookies 'Z orig orig where + addSetCookies _ = id + +instance {-# OVERLAPPABLE #-} + ( Functor m + , AddSetCookies n (m old) (m cookied) + , AddHeader "Set-Cookie" SetCookie cookied new + ) => AddSetCookies ('S n) (m old) (m new) where + addSetCookies (mCookie `SetCookieCons` rest) oldVal = + case mCookie of + Nothing -> noHeader <$> addSetCookies rest oldVal + Just cookie -> addHeader cookie <$> addSetCookies rest oldVal + +instance {-# OVERLAPS #-} + (AddSetCookies ('S n) a a', AddSetCookies ('S n) b b') + => AddSetCookies ('S n) (a :<|> b) (a' :<|> b') where + addSetCookies cookies (a :<|> b) = addSetCookies cookies a :<|> addSetCookies cookies b + +-- | for @servant <0.11@ +instance + AddSetCookies ('S n) Application Application where + addSetCookies cookies r request respond + = r request $ respond . mapResponseHeaders (++ mkHeaders cookies) + +-- | for @servant >=0.11@ +instance + AddSetCookies ('S n) (Tagged m Application) (Tagged m Application) where + addSetCookies cookies r = Tagged $ \request respond -> + unTagged r request $ respond . mapResponseHeaders (++ mkHeaders cookies) + +mkHeaders :: SetCookieList x -> [HTTP.Header] +mkHeaders x = ("Set-Cookie",) <$> mkCookies x + where + mkCookies :: forall y. SetCookieList y -> [BS.ByteString] + mkCookies SetCookieNil = [] + mkCookies (SetCookieCons Nothing rest) = mkCookies rest + mkCookies (SetCookieCons (Just y) rest) + = toByteString (renderSetCookie y) : mkCookies rest diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/BasicAuth.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/BasicAuth.hs new file mode 100644 index 00000000..f35eb6f7 --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/BasicAuth.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE CPP #-} +module Servant.Auth.Server.Internal.BasicAuth where + +#if !MIN_VERSION_servant_server(0,16,0) +#define ServerError ServantErr +#endif + +import qualified Data.ByteString as BS +import Servant (BasicAuthData (..), + ServerError (..), err401) +import Servant.Server.Internal.BasicAuth (decodeBAHdr, + mkBAChallengerHdr) + +import Servant.Auth.Server.Internal.Types + +-- | A 'ServerError' that asks the client to authenticate via Basic +-- Authentication, should be invoked by an application whenever +-- appropriate. The argument is the realm. +wwwAuthenticatedErr :: BS.ByteString -> ServerError +wwwAuthenticatedErr realm = err401 { errHeaders = [mkBAChallengerHdr realm] } + +-- | A type holding the configuration for Basic Authentication. +-- It is defined as a type family with no arguments, so that +-- it can be instantiated to whatever type you need to +-- authenticate your users (use @type instance BasicAuthCfg = ...@). +-- +-- Note that the instantiation is application-wide, +-- i.e. there can be only one instance. +-- As a consequence, it should not be instantiated in a library. +-- +-- Basic Authentication expects an element of type 'BasicAuthCfg' +-- to be in the 'Context'; that element is then passed automatically +-- to the instance of 'FromBasicAuthData' together with the +-- authentication data obtained from the client. +-- +-- If you do not need a configuration for Basic Authentication, +-- you can use just @BasicAuthCfg = ()@, and recall to also +-- add @()@ to the 'Context'. +-- A basic but more interesting example is to take as 'BasicAuthCfg' +-- a list of authorised username/password pairs: +-- +-- > deriving instance Eq BasicAuthData +-- > type instance BasicAuthCfg = [BasicAuthData] +-- > instance FromBasicAuthData User where +-- > fromBasicAuthData authData authCfg = +-- > if elem authData authCfg then ... +type family BasicAuthCfg + +class FromBasicAuthData a where + -- | Whether the username exists and the password is correct. + -- Note that, rather than passing a 'Pass' to the function, we pass a + -- function that checks an 'EncryptedPass'. This is to make sure you don't + -- accidentally do something untoward with the password, like store it. + fromBasicAuthData :: BasicAuthData -> BasicAuthCfg -> IO (AuthResult a) + +basicAuthCheck :: FromBasicAuthData usr => BasicAuthCfg -> AuthCheck usr +basicAuthCheck cfg = AuthCheck $ \req -> case decodeBAHdr req of + Nothing -> return Indefinite + Just baData -> fromBasicAuthData baData cfg diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Class.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Class.hs new file mode 100644 index 00000000..2f13bbc3 --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Class.hs @@ -0,0 +1,72 @@ +{-# LANGUAGE UndecidableInstances #-} +module Servant.Auth.Server.Internal.Class where + +import Servant.Auth +import Data.Monoid +import Servant hiding (BasicAuth) + +import Servant.Auth.JWT +import Servant.Auth.Server.Internal.Types +import Servant.Auth.Server.Internal.ConfigTypes +import Servant.Auth.Server.Internal.BasicAuth +import Servant.Auth.Server.Internal.Cookie +import Servant.Auth.Server.Internal.JWT (jwtAuthCheck) + +-- | @IsAuth a ctx v@ indicates that @a@ is an auth type that expects all +-- elements of @ctx@ to be the in the Context and whose authentication check +-- returns an @AuthCheck v@. +class IsAuth a v where + type family AuthArgs a :: [*] + runAuth :: proxy a -> proxy v -> Unapp (AuthArgs a) (AuthCheck v) + +instance FromJWT usr => IsAuth Cookie usr where + type AuthArgs Cookie = '[CookieSettings, JWTSettings] + runAuth _ _ = cookieAuthCheck + +instance FromJWT usr => IsAuth JWT usr where + type AuthArgs JWT = '[JWTSettings] + runAuth _ _ = jwtAuthCheck + +instance FromBasicAuthData usr => IsAuth BasicAuth usr where + type AuthArgs BasicAuth = '[BasicAuthCfg] + runAuth _ _ = basicAuthCheck + +-- * Helper + +class AreAuths (as :: [*]) (ctxs :: [*]) v where + runAuths :: proxy as -> Context ctxs -> AuthCheck v + +instance AreAuths '[] ctxs v where + runAuths _ _ = mempty + +instance ( AuthCheck v ~ App (AuthArgs a) (Unapp (AuthArgs a) (AuthCheck v)) + , IsAuth a v + , AreAuths as ctxs v + , AppCtx ctxs (AuthArgs a) (Unapp (AuthArgs a) (AuthCheck v)) + ) => AreAuths (a ': as) ctxs v where + runAuths _ ctxs = go <> runAuths (Proxy :: Proxy as) ctxs + where + go = appCtx (Proxy :: Proxy (AuthArgs a)) + ctxs + (runAuth (Proxy :: Proxy a) (Proxy :: Proxy v)) + +type family Unapp ls res where + Unapp '[] res = res + Unapp (arg1 ': rest) res = arg1 -> Unapp rest res + +type family App ls res where + App '[] res = res + App (arg1 ': rest) (arg1 -> res) = App rest res + +-- | @AppCtx@ applies the function @res@ to the arguments in @ls@ by taking the +-- values from the Context provided. +class AppCtx ctx ls res where + appCtx :: proxy ls -> Context ctx -> res -> App ls res + +instance ( HasContextEntry ctxs ctx + , AppCtx ctxs rest res + ) => AppCtx ctxs (ctx ': rest) (ctx -> res) where + appCtx _ ctx fn = appCtx (Proxy :: Proxy rest) ctx $ fn $ getContextEntry ctx + +instance AppCtx ctx '[] res where + appCtx _ _ r = r diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs new file mode 100644 index 00000000..83e5784d --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ConfigTypes.hs @@ -0,0 +1,127 @@ +module Servant.Auth.Server.Internal.ConfigTypes + ( module Servant.Auth.Server.Internal.ConfigTypes + , Servant.API.IsSecure(..) + ) where + +import Crypto.JOSE as Jose +import Crypto.JWT as Jose +import qualified Data.ByteString as BS +import Data.Default.Class +import Data.Time +import GHC.Generics (Generic) +import Servant.API (IsSecure(..)) + +data IsMatch = Matches | DoesNotMatch + deriving (Eq, Show, Read, Generic, Ord) + +data IsPasswordCorrect = PasswordCorrect | PasswordIncorrect + deriving (Eq, Show, Read, Generic, Ord) + +-- The @SameSite@ attribute of cookies determines whether cookies will be sent +-- on cross-origin requests. +-- +-- See +-- for more information. +data SameSite = AnySite | SameSiteStrict | SameSiteLax + deriving (Eq, Show, Read, Generic, Ord) + +-- | @JWTSettings@ are used to generate cookies, and to verify JWTs. +data JWTSettings = JWTSettings + { + -- | Key used to sign JWT. + signingKey :: Jose.JWK + -- | Algorithm used to sign JWT. + , jwtAlg :: Maybe Jose.Alg + -- | Keys used to validate JWT. + , validationKeys :: Jose.JWKSet + -- | An @aud@ predicate. The @aud@ is a string or URI that identifies the + -- intended recipient of the JWT. + , audienceMatches :: Jose.StringOrURI -> IsMatch + } deriving (Generic) + +-- | A @JWTSettings@ where the audience always matches. +defaultJWTSettings :: Jose.JWK -> JWTSettings +defaultJWTSettings k = JWTSettings + { signingKey = k + , jwtAlg = Nothing + , validationKeys = Jose.JWKSet [k] + , audienceMatches = const Matches } + +-- | The policies to use when generating cookies. +-- +-- If *both* 'cookieMaxAge' and 'cookieExpires' are @Nothing@, browsers will +-- treat the cookie as a *session cookie*. These will be deleted when the +-- browser is closed. +-- +-- Note that having the setting @Secure@ may cause testing failures if you are +-- not testing over HTTPS. +data CookieSettings = CookieSettings + { + -- | 'Secure' means browsers will only send cookies over HTTPS. Default: + -- @Secure@. + cookieIsSecure :: !IsSecure + -- | How long from now until the cookie expires. Default: @Nothing@. + , cookieMaxAge :: !(Maybe DiffTime) + -- | At what time the cookie expires. Default: @Nothing@. + , cookieExpires :: !(Maybe UTCTime) + -- | The URL path and sub-paths for which this cookie is used. Default: @Just "/"@. + , cookiePath :: !(Maybe BS.ByteString) + -- | Domain name, if set cookie also allows subdomains. Default: @Nothing@. + , cookieDomain :: !(Maybe BS.ByteString) + -- | 'SameSite' settings. Default: @SameSiteLax@. + , cookieSameSite :: !SameSite + -- | What name to use for the cookie used for the session. + , sessionCookieName :: !BS.ByteString + -- | The optional settings to use for XSRF protection. Default: @Just def@. + , cookieXsrfSetting :: !(Maybe XsrfCookieSettings) + } deriving (Eq, Show, Generic) + +instance Default CookieSettings where + def = defaultCookieSettings + +defaultCookieSettings :: CookieSettings +defaultCookieSettings = CookieSettings + { cookieIsSecure = Secure + , cookieMaxAge = Nothing + , cookieExpires = Nothing + , cookiePath = Just "/" + , cookieDomain = Nothing + , cookieSameSite = SameSiteLax + , sessionCookieName = "JWT-Cookie" + , cookieXsrfSetting = Just def + } + +-- | The policies to use when generating and verifying XSRF cookies +data XsrfCookieSettings = XsrfCookieSettings + { + -- | What name to use for the cookie used for XSRF protection. + xsrfCookieName :: !BS.ByteString + -- | What path to use for the cookie used for XSRF protection. Default @Just "/"@. + , xsrfCookiePath :: !(Maybe BS.ByteString) + -- | What name to use for the header used for XSRF protection. + , xsrfHeaderName :: !BS.ByteString + -- | Exclude GET request method from XSRF protection. + , xsrfExcludeGet :: !Bool + } deriving (Eq, Show, Generic) + +instance Default XsrfCookieSettings where + def = defaultXsrfCookieSettings + +defaultXsrfCookieSettings :: XsrfCookieSettings +defaultXsrfCookieSettings = XsrfCookieSettings + { xsrfCookieName = "XSRF-TOKEN" + , xsrfCookiePath = Just "/" + , xsrfHeaderName = "X-XSRF-TOKEN" + , xsrfExcludeGet = False + } + +------------------------------------------------------------------------------ +-- Internal {{{ + +jwtSettingsToJwtValidationSettings :: JWTSettings -> Jose.JWTValidationSettings +jwtSettingsToJwtValidationSettings s + = defaultJWTValidationSettings (toBool <$> audienceMatches s) + where + toBool Matches = True + toBool DoesNotMatch = False +-- }}} diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Cookie.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Cookie.hs new file mode 100644 index 00000000..a91b42de --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Cookie.hs @@ -0,0 +1,182 @@ +{-# LANGUAGE CPP #-} +module Servant.Auth.Server.Internal.Cookie where + +import Blaze.ByteString.Builder (toByteString) +import Control.Monad.Except +import Control.Monad.Reader +import qualified Crypto.JOSE as Jose +import qualified Crypto.JWT as Jose +import Data.ByteArray (constEq) +import qualified Data.ByteString as BS +import qualified Data.ByteString.Base64 as BS64 +import qualified Data.ByteString.Lazy as BSL +import Data.CaseInsensitive (mk) +import Data.Maybe (fromMaybe) +import Data.Time.Calendar (Day(..)) +import Data.Time.Clock (UTCTime(..), secondsToDiffTime) +import Network.HTTP.Types (methodGet) +import Network.HTTP.Types.Header(hCookie) +import Network.Wai (Request, requestHeaders, requestMethod) +import Servant (AddHeader, addHeader) +import System.Entropy (getEntropy) +import Web.Cookie + +import Servant.Auth.JWT (FromJWT (decodeJWT), ToJWT) +import Servant.Auth.Server.Internal.ConfigTypes +import Servant.Auth.Server.Internal.JWT (makeJWT, verifyJWT) +import Servant.Auth.Server.Internal.Types + + +cookieAuthCheck :: FromJWT usr => CookieSettings -> JWTSettings -> AuthCheck usr +cookieAuthCheck ccfg jwtSettings = do + req <- ask + jwtCookie <- maybe mempty return $ do + cookies' <- lookup hCookie $ requestHeaders req + let cookies = parseCookies cookies' + -- Apply the XSRF check if enabled. + guard $ fromMaybe True $ do + xsrfCookieCfg <- xsrfCheckRequired ccfg req + return $ xsrfCookieAuthCheck xsrfCookieCfg req cookies + -- session cookie *must* be HttpOnly and Secure + lookup (sessionCookieName ccfg) cookies + verifiedJWT <- liftIO $ verifyJWT jwtSettings jwtCookie + case verifiedJWT of + Nothing -> mzero + Just v -> return v + +xsrfCheckRequired :: CookieSettings -> Request -> Maybe XsrfCookieSettings +xsrfCheckRequired cookieSettings req = do + xsrfCookieCfg <- cookieXsrfSetting cookieSettings + let disableForGetReq = xsrfExcludeGet xsrfCookieCfg && requestMethod req == methodGet + guard $ not disableForGetReq + return xsrfCookieCfg + +xsrfCookieAuthCheck :: XsrfCookieSettings -> Request -> [(BS.ByteString, BS.ByteString)] -> Bool +xsrfCookieAuthCheck xsrfCookieCfg req cookies = fromMaybe False $ do + xsrfCookie <- lookup (xsrfCookieName xsrfCookieCfg) cookies + xsrfHeader <- lookup (mk $ xsrfHeaderName xsrfCookieCfg) $ requestHeaders req + return $ xsrfCookie `constEq` xsrfHeader + +-- | Makes a cookie to be used for XSRF. +makeXsrfCookie :: CookieSettings -> IO SetCookie +makeXsrfCookie cookieSettings = case cookieXsrfSetting cookieSettings of + Just xsrfCookieSettings -> makeRealCookie xsrfCookieSettings + Nothing -> return $ noXsrfTokenCookie cookieSettings + where + makeRealCookie xsrfCookieSettings = do + xsrfValue <- BS64.encode <$> getEntropy 32 + return + $ applyXsrfCookieSettings xsrfCookieSettings + $ applyCookieSettings cookieSettings + $ def{ setCookieValue = xsrfValue } + + +-- | Alias for 'makeXsrfCookie'. +makeCsrfCookie :: CookieSettings -> IO SetCookie +makeCsrfCookie = makeXsrfCookie +{-# DEPRECATED makeCsrfCookie "Use makeXsrfCookie instead" #-} + + +-- | Makes a cookie with session information. +makeSessionCookie :: ToJWT v => CookieSettings -> JWTSettings -> v -> IO (Maybe SetCookie) +makeSessionCookie cookieSettings jwtSettings v = do + ejwt <- makeJWT v jwtSettings (cookieExpires cookieSettings) + case ejwt of + Left _ -> return Nothing + Right jwt -> return + $ Just + $ applySessionCookieSettings cookieSettings + $ applyCookieSettings cookieSettings + $ def{ setCookieValue = BSL.toStrict jwt } + +noXsrfTokenCookie :: CookieSettings -> SetCookie +noXsrfTokenCookie cookieSettings = + applyCookieSettings cookieSettings $ def{ setCookieName = "NO-XSRF-TOKEN", setCookieValue = "" } + +applyCookieSettings :: CookieSettings -> SetCookie -> SetCookie +applyCookieSettings cookieSettings setCookie = setCookie + { setCookieMaxAge = cookieMaxAge cookieSettings + , setCookieExpires = cookieExpires cookieSettings + , setCookiePath = cookiePath cookieSettings + , setCookieDomain = cookieDomain cookieSettings + , setCookieSecure = case cookieIsSecure cookieSettings of + Secure -> True + NotSecure -> False + } + +applyXsrfCookieSettings :: XsrfCookieSettings -> SetCookie -> SetCookie +applyXsrfCookieSettings xsrfCookieSettings setCookie = setCookie + { setCookieName = xsrfCookieName xsrfCookieSettings + , setCookiePath = xsrfCookiePath xsrfCookieSettings + , setCookieHttpOnly = False + } + +applySessionCookieSettings :: CookieSettings -> SetCookie -> SetCookie +applySessionCookieSettings cookieSettings setCookie = setCookie + { setCookieName = sessionCookieName cookieSettings + , setCookieSameSite = case cookieSameSite cookieSettings of + AnySite -> anySite + SameSiteStrict -> Just sameSiteStrict + SameSiteLax -> Just sameSiteLax + , setCookieHttpOnly = True + } + where +#if MIN_VERSION_cookie(0,4,5) + anySite = Just sameSiteNone +#else + anySite = Nothing +#endif + +-- | For a JWT-serializable session, returns a function that decorates a +-- provided response object with XSRF and session cookies. This should be used +-- when a user successfully authenticates with credentials. +acceptLogin :: ( ToJWT session + , AddHeader "Set-Cookie" SetCookie response withOneCookie + , AddHeader "Set-Cookie" SetCookie withOneCookie withTwoCookies ) + => CookieSettings + -> JWTSettings + -> session + -> IO (Maybe (response -> withTwoCookies)) +acceptLogin cookieSettings jwtSettings session = do + mSessionCookie <- makeSessionCookie cookieSettings jwtSettings session + case mSessionCookie of + Nothing -> pure Nothing + Just sessionCookie -> do + xsrfCookie <- makeXsrfCookie cookieSettings + return $ Just $ addHeader sessionCookie . addHeader xsrfCookie + +-- | Arbitrary cookie expiry time set back in history after unix time 0 +expireTime :: UTCTime +expireTime = UTCTime (ModifiedJulianDay 50000) 0 + +-- | Adds headers to a response that clears all session cookies +-- | using max-age and expires cookie attributes. +clearSession :: ( AddHeader "Set-Cookie" SetCookie response withOneCookie + , AddHeader "Set-Cookie" SetCookie withOneCookie withTwoCookies ) + => CookieSettings + -> response + -> withTwoCookies +clearSession cookieSettings = addHeader clearedSessionCookie . addHeader clearedXsrfCookie + where + -- According to RFC6265 max-age takes precedence, but IE/Edge ignore it completely so we set both + cookieSettingsExpires = cookieSettings + { cookieExpires = Just expireTime + , cookieMaxAge = Just (secondsToDiffTime 0) + } + clearedSessionCookie = applySessionCookieSettings cookieSettingsExpires $ applyCookieSettings cookieSettingsExpires def + clearedXsrfCookie = case cookieXsrfSetting cookieSettings of + Just xsrfCookieSettings -> applyXsrfCookieSettings xsrfCookieSettings $ applyCookieSettings cookieSettingsExpires def + Nothing -> noXsrfTokenCookie cookieSettingsExpires + +makeSessionCookieBS :: ToJWT v => CookieSettings -> JWTSettings -> v -> IO (Maybe BS.ByteString) +makeSessionCookieBS a b c = fmap (toByteString . renderSetCookie) <$> makeSessionCookie a b c + +-- | Alias for 'makeSessionCookie'. +makeCookie :: ToJWT v => CookieSettings -> JWTSettings -> v -> IO (Maybe SetCookie) +makeCookie = makeSessionCookie +{-# DEPRECATED makeCookie "Use makeSessionCookie instead" #-} + +-- | Alias for 'makeSessionCookieBS'. +makeCookieBS :: ToJWT v => CookieSettings -> JWTSettings -> v -> IO (Maybe BS.ByteString) +makeCookieBS = makeSessionCookieBS +{-# DEPRECATED makeCookieBS "Use makeSessionCookieBS instead" #-} diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/FormLogin.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/FormLogin.hs new file mode 100644 index 00000000..5301640c --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/FormLogin.hs @@ -0,0 +1,3 @@ +module Servant.Auth.Server.Internal.FormLogin where + + diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs new file mode 100644 index 00000000..57c0630c --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/JWT.hs @@ -0,0 +1,71 @@ +module Servant.Auth.Server.Internal.JWT where + +import Control.Lens +import Control.Monad.Except +import Control.Monad.Reader +import qualified Crypto.JOSE as Jose +import qualified Crypto.JWT as Jose +import Data.Aeson (FromJSON, Result (..), ToJSON, fromJSON, + toJSON) +import Data.ByteArray (constEq) +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BSL +import qualified Data.HashMap.Strict as HM +import Data.Maybe (fromMaybe) +import qualified Data.Text as T +import Data.Time (UTCTime) +import Network.Wai (requestHeaders) + +import Servant.Auth.JWT (FromJWT(..), ToJWT(..)) +import Servant.Auth.Server.Internal.ConfigTypes +import Servant.Auth.Server.Internal.Types + + +-- | A JWT @AuthCheck@. You likely won't need to use this directly unless you +-- are protecting a @Raw@ endpoint. +jwtAuthCheck :: FromJWT usr => JWTSettings -> AuthCheck usr +jwtAuthCheck jwtSettings = do + req <- ask + token <- maybe mempty return $ do + authHdr <- lookup "Authorization" $ requestHeaders req + let bearer = "Bearer " + (mbearer, rest) = BS.splitAt (BS.length bearer) authHdr + guard (mbearer `constEq` bearer) + return rest + verifiedJWT <- liftIO $ verifyJWT jwtSettings token + case verifiedJWT of + Nothing -> mzero + Just v -> return v + +-- | Creates a JWT containing the specified data. The data is stored in the +-- @dat@ claim. The 'Maybe UTCTime' argument indicates the time at which the +-- token expires. +makeJWT :: ToJWT a + => a -> JWTSettings -> Maybe UTCTime -> IO (Either Jose.Error BSL.ByteString) +makeJWT v cfg expiry = runExceptT $ do + bestAlg <- Jose.bestJWSAlg $ signingKey cfg + let alg = fromMaybe bestAlg $ jwtAlg cfg + ejwt <- Jose.signClaims (signingKey cfg) + (Jose.newJWSHeader ((), alg)) + (addExp $ encodeJWT v) + + return $ Jose.encodeCompact ejwt + where + addExp claims = case expiry of + Nothing -> claims + Just e -> claims & Jose.claimExp ?~ Jose.NumericDate e + + +verifyJWT :: FromJWT a => JWTSettings -> BS.ByteString -> IO (Maybe a) +verifyJWT jwtCfg input = do + verifiedJWT <- liftIO $ runExceptT $ do + unverifiedJWT <- Jose.decodeCompact (BSL.fromStrict input) + Jose.verifyClaims + (jwtSettingsToJwtValidationSettings jwtCfg) + (validationKeys jwtCfg) + unverifiedJWT + return $ case verifiedJWT of + Left (_ :: Jose.JWTError) -> Nothing + Right v -> case decodeJWT v of + Left _ -> Nothing + Right v' -> Just v' \ No newline at end of file diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ThrowAll.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ThrowAll.hs new file mode 100644 index 00000000..956af6b8 --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/ThrowAll.hs @@ -0,0 +1,49 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE UndecidableInstances #-} +module Servant.Auth.Server.Internal.ThrowAll where + +#if !MIN_VERSION_servant_server(0,16,0) +#define ServerError ServantErr +#endif + +import Control.Monad.Error.Class +import Data.Tagged (Tagged (..)) +import Servant ((:<|>) (..), ServerError(..)) +import Network.HTTP.Types +import Network.Wai + +import qualified Data.ByteString.Char8 as BS + +class ThrowAll a where + -- | 'throwAll' is a convenience function to throw errors across an entire + -- sub-API + -- + -- + -- > throwAll err400 :: Handler a :<|> Handler b :<|> Handler c + -- > == throwError err400 :<|> throwError err400 :<|> err400 + throwAll :: ServerError -> a + +instance (ThrowAll a, ThrowAll b) => ThrowAll (a :<|> b) where + throwAll e = throwAll e :<|> throwAll e + +-- Really this shouldn't be necessary - ((->) a) should be an instance of +-- MonadError, no? +instance {-# OVERLAPPING #-} ThrowAll b => ThrowAll (a -> b) where + throwAll e = const $ throwAll e + +instance {-# OVERLAPPABLE #-} (MonadError ServerError m) => ThrowAll (m a) where + throwAll = throwError + +-- | for @servant <0.11@ +instance {-# OVERLAPPING #-} ThrowAll Application where + throwAll e _req respond + = respond $ responseLBS (mkStatus (errHTTPCode e) (BS.pack $ errReasonPhrase e)) + (errHeaders e) + (errBody e) + +-- | for @servant >=0.11@ +instance {-# OVERLAPPING #-} MonadError ServerError m => ThrowAll (Tagged m Application) where + throwAll e = Tagged $ \_req respond -> + respond $ responseLBS (mkStatus (errHTTPCode e) (BS.pack $ errReasonPhrase e)) + (errHeaders e) + (errBody e) diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Types.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Types.hs new file mode 100644 index 00000000..8e9e91f1 --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/Types.hs @@ -0,0 +1,112 @@ +{-# LANGUAGE CPP #-} +module Servant.Auth.Server.Internal.Types where + +import Control.Applicative +import Control.Monad.Reader +import Control.Monad.Time +import Data.Monoid (Monoid (..)) +import Data.Semigroup (Semigroup (..)) +import Data.Time (getCurrentTime) +import GHC.Generics (Generic) +import Network.Wai (Request) + +import qualified Control.Monad.Fail as Fail + +-- | The result of an authentication attempt. +data AuthResult val + = BadPassword + | NoSuchUser + -- | Authentication succeeded. + | Authenticated val + -- | If an authentication procedure cannot be carried out - if for example it + -- expects a password and username in a header that is not present - + -- @Indefinite@ is returned. This indicates that other authentication + -- methods should be tried. + | Indefinite + deriving (Eq, Show, Read, Generic, Ord, Functor, Traversable, Foldable) + +instance Semigroup (AuthResult val) where + Indefinite <> y = y + x <> _ = x + +instance Monoid (AuthResult val) where + mempty = Indefinite + mappend = (<>) + +instance Applicative AuthResult where + pure = return + (<*>) = ap + +instance Monad AuthResult where + return = Authenticated + Authenticated v >>= f = f v + BadPassword >>= _ = BadPassword + NoSuchUser >>= _ = NoSuchUser + Indefinite >>= _ = Indefinite + +instance Alternative AuthResult where + empty = mzero + (<|>) = mplus + +instance MonadPlus AuthResult where + mzero = mempty + mplus = (<>) + + +-- | An @AuthCheck@ is the function used to decide the authentication status +-- (the 'AuthResult') of a request. Different @AuthCheck@s may be combined as a +-- Monoid or Alternative; the semantics of this is that the *first* +-- non-'Indefinite' result from left to right is used and the rest are ignored. +newtype AuthCheck val = AuthCheck + { runAuthCheck :: Request -> IO (AuthResult val) } + deriving (Generic, Functor) + +instance Semigroup (AuthCheck val) where + AuthCheck f <> AuthCheck g = AuthCheck $ \x -> do + fx <- f x + case fx of + Indefinite -> g x + r -> pure r + +instance Monoid (AuthCheck val) where + mempty = AuthCheck $ const $ return mempty + mappend = (<>) + +instance Applicative AuthCheck where + pure = return + (<*>) = ap + +instance Monad AuthCheck where + return = AuthCheck . return . return . return + AuthCheck ac >>= f = AuthCheck $ \req -> do + aresult <- ac req + case aresult of + Authenticated usr -> runAuthCheck (f usr) req + BadPassword -> return BadPassword + NoSuchUser -> return NoSuchUser + Indefinite -> return Indefinite + +#if !MIN_VERSION_base(4,13,0) + fail = Fail.fail +#endif + +instance Fail.MonadFail AuthCheck where + fail _ = AuthCheck . const $ return Indefinite + +instance MonadReader Request AuthCheck where + ask = AuthCheck $ \x -> return (Authenticated x) + local f (AuthCheck check) = AuthCheck $ \req -> check (f req) + +instance MonadIO AuthCheck where + liftIO action = AuthCheck $ const $ Authenticated <$> action + +instance MonadTime AuthCheck where + currentTime = liftIO getCurrentTime + +instance Alternative AuthCheck where + empty = mzero + (<|>) = mplus + +instance MonadPlus AuthCheck where + mzero = mempty + mplus = (<>) diff --git a/servant-auth/servant-auth-server/src/Servant/Auth/Server/SetCookieOrphan.hs b/servant-auth/servant-auth-server/src/Servant/Auth/Server/SetCookieOrphan.hs new file mode 100644 index 00000000..de87ad27 --- /dev/null +++ b/servant-auth/servant-auth-server/src/Servant/Auth/Server/SetCookieOrphan.hs @@ -0,0 +1,3 @@ +module Servant.Auth.Server.SetCookieOrphan + {-# DEPRECATED "instance exists in http-api-data-0.3.9. This module will be removed in next major release." #-} + () where diff --git a/servant-auth/servant-auth-server/test/Servant/Auth/ServerSpec.hs b/servant-auth/servant-auth-server/test/Servant/Auth/ServerSpec.hs new file mode 100644 index 00000000..75257f34 --- /dev/null +++ b/servant-auth/servant-auth-server/test/Servant/Auth/ServerSpec.hs @@ -0,0 +1,600 @@ +{-# LANGUAGE CPP #-} +module Servant.Auth.ServerSpec (spec) where + +#if !MIN_VERSION_servant_server(0,16,0) +#define ServerError ServantErr +#endif + +import Control.Lens +import Control.Monad.Except (runExceptT) +import Control.Monad.IO.Class (liftIO) +import Crypto.JOSE (Alg (HS256, None), Error, + JWK, JWSHeader, + KeyMaterialGenParam (OctGenParam), + ToCompact, encodeCompact, + genJWK, newJWSHeader) +import Crypto.JWT (Audience (..), ClaimsSet, + NumericDate (NumericDate), + SignedJWT, + claimAud, claimNbf, + signClaims, + emptyClaimsSet, + unregisteredClaims) +import Data.Aeson (FromJSON, ToJSON, Value, + toJSON, encode) +import Data.Aeson.Lens (_JSON) +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BSL +import Data.CaseInsensitive (mk) +import Data.Foldable (find) +import Data.Monoid +import Data.Time +import Data.Time.Clock (getCurrentTime) +import GHC.Generics (Generic) +import Network.HTTP.Client (cookie_http_only, + cookie_name, cookie_value, + cookie_expiry_time, + destroyCookieJar) +import Network.HTTP.Types (Status, status200, + status401) +import Network.Wai (responseLBS) +import Network.Wai.Handler.Warp (testWithApplication) +import Network.Wreq (Options, auth, basicAuth, + cookieExpiryTime, cookies, + defaults, get, getWith, postWith, + header, oauth2Bearer, + responseBody, + responseCookieJar, + responseHeader, + responseStatus) +import Network.Wreq.Types (Postable(..)) +import Servant hiding (BasicAuth, + IsSecure (..), header) +import Servant.Auth.Server +import Servant.Auth.Server.Internal.Cookie (expireTime) +import Servant.Auth.Server.SetCookieOrphan () +#if MIN_VERSION_servant_server(0,15,0) +import qualified Servant.Types.SourceT as S +#endif +import System.IO.Unsafe (unsafePerformIO) +import Test.Hspec +import Test.QuickCheck +import qualified Network.HTTP.Client as HCli + + + +spec :: Spec +spec = do + authSpec + cookieAuthSpec + jwtAuthSpec + throwAllSpec + basicAuthSpec + +------------------------------------------------------------------------------ +-- * Auth {{{ + +authSpec :: Spec +authSpec + = describe "The Auth combinator" + $ around (testWithApplication . return $ app jwtAndCookieApi) $ do + + it "returns a 401 if all authentications are Indefinite" $ \port -> do + get (url port) `shouldHTTPErrorWith` status401 + + it "succeeds if one authentication suceeds" $ \port -> property $ + \(user :: User) -> do + jwt <- makeJWT user jwtCfg Nothing + opts <- addJwtToHeader jwt + resp <- getWith opts (url port) + resp ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + it "fails (403) if one authentication fails" $ const $ + pendingWith "Authentications don't yet fail, only are Indefinite" + + it "doesn't clobber pre-existing response headers" $ \port -> property $ + \(user :: User) -> do + jwt <- makeJWT user jwtCfg Nothing + opts <- addJwtToHeader jwt + resp <- getWith opts (url port ++ "/header") + resp ^. responseHeader "Blah" `shouldBe` "1797" + resp ^. responseHeader "Set-Cookie" `shouldSatisfy` (/= "") + + context "Raw" $ do + + it "gets the response body" $ \port -> property $ \(user :: User) -> do + jwt <- makeJWT user jwtCfg Nothing + opts <- addJwtToHeader jwt + resp <- getWith opts (url port ++ "/raw") + resp ^. responseBody `shouldBe` "how are you?" + + it "doesn't clobber pre-existing reponse headers" $ \port -> property $ + \(user :: User) -> do + jwt <- makeJWT user jwtCfg Nothing + opts <- addJwtToHeader jwt + resp <- getWith opts (url port ++ "/raw") + resp ^. responseHeader "hi" `shouldBe` "there" + resp ^. responseHeader "Set-Cookie" `shouldSatisfy` (/= "") + + + context "Setting cookies" $ do + + it "sets cookies that it itself accepts" $ \port -> property $ \user -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims $ toJSON user) + opts' <- addJwtToCookie cookieCfg jwt + let opts = addCookie (opts' & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ ["blah"]) + (xsrfField xsrfCookieName cookieCfg <> "=blah") + resp <- getWith opts (url port) + let (cookieJar:_) = resp ^.. responseCookieJar + Just xxsrf = find (\x -> cookie_name x == xsrfField xsrfCookieName cookieCfg) + $ destroyCookieJar cookieJar + opts2 = defaults + & cookies .~ Just cookieJar + & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ [cookie_value xxsrf] + resp2 <- getWith opts2 (url port) + resp2 ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + it "uses the Expiry from the configuration" $ \port -> property $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims $ toJSON user) + opts' <- addJwtToCookie cookieCfg jwt + let opts = addCookie (opts' & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ ["blah"]) + (xsrfField xsrfCookieName cookieCfg <> "=blah") + resp <- getWith opts (url port) + let (cookieJar:_) = resp ^.. responseCookieJar + Just xxsrf = find (\x -> cookie_name x == xsrfField xsrfCookieName cookieCfg) + $ destroyCookieJar cookieJar + xxsrf ^. cookieExpiryTime `shouldBe` future + + it "sets the token cookie as HttpOnly" $ \port -> property $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims $ toJSON user) + opts' <- addJwtToCookie cookieCfg jwt + let opts = addCookie (opts' & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ ["blah"]) + (xsrfField xsrfCookieName cookieCfg <> "=blah") + resp <- getWith opts (url port) + let (cookieJar:_) = resp ^.. responseCookieJar + Just token = find (\x -> cookie_name x == sessionCookieName cookieCfg) + $ destroyCookieJar cookieJar + cookie_http_only token `shouldBe` True + + + +-- }}} +------------------------------------------------------------------------------ +-- * Cookie Auth {{{ + +cookieAuthSpec :: Spec +cookieAuthSpec + = describe "The Auth combinator" $ do + describe "With XSRF check" $ + around (testWithApplication . return $ app cookieOnlyApi) $ do + + it "fails if XSRF header and cookie don't match" $ \port -> property + $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts' <- addJwtToCookie cookieCfg jwt + let opts = addCookie (opts' & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ ["blah"]) + (xsrfField xsrfCookieName cookieCfg <> "=blerg") + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "fails with no XSRF header or cookie" $ \port -> property + $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts <- addJwtToCookie cookieCfg jwt + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "succeeds if XSRF header and cookie match, and JWT is valid" $ \port -> property + $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts' <- addJwtToCookie cookieCfg jwt + let opts = addCookie (opts' & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ ["blah"]) + (xsrfField xsrfCookieName cookieCfg <> "=blah") + resp <- getWith opts (url port) + resp ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + it "sets and clears the right cookies" $ \port -> property + $ \(user :: User) -> do + let optsFromResp resp = + let jar = resp ^. responseCookieJar + Just xsrfCookieValue = cookie_value <$> find (\c -> cookie_name c == xsrfField xsrfCookieName cookieCfg) (destroyCookieJar jar) + in defaults + & cookies .~ Just jar -- real cookie jars aren't updated by being replaced + & header (mk (xsrfField xsrfHeaderName cookieCfg)) .~ [xsrfCookieValue] + + resp <- postWith defaults (url port ++ "/login") user + (resp ^. responseCookieJar) `shouldMatchCookieNames` + [ sessionCookieName cookieCfg + , xsrfField xsrfCookieName cookieCfg + ] + let loggedInOpts = optsFromResp resp + + resp <- getWith loggedInOpts (url port) + resp ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + -- logout + resp <- getWith loggedInOpts (url port ++ "/logout") + + -- assert cookies were expired + now <- getCurrentTime + let assertCookie c = now >= cookie_expiry_time c + all assertCookie (destroyCookieJar (resp ^. responseCookieJar)) `shouldBe` True + + let loggedOutOpts = optsFromResp resp + getWith loggedOutOpts (url port) `shouldHTTPErrorWith` status401 + + describe "With no XSRF check for GET requests" $ let + noXsrfGet xsrfCfg = xsrfCfg { xsrfExcludeGet = True } + cookieCfgNoXsrfGet = cookieCfg { cookieXsrfSetting = fmap noXsrfGet $ cookieXsrfSetting cookieCfg } + in around (testWithApplication . return $ appWithCookie cookieOnlyApi cookieCfgNoXsrfGet) $ do + + it "succeeds with no XSRF header or cookie for GET" $ \port -> property + $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts <- addJwtToCookie cookieCfgNoXsrfGet jwt + resp <- getWith opts (url port) + resp ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + it "fails with no XSRF header or cookie for POST" $ \port -> property + $ \(user :: User) number -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts <- addJwtToCookie cookieCfgNoXsrfGet jwt + postWith opts (url port) (toJSON (number :: Int)) `shouldHTTPErrorWith` status401 + + describe "With no XSRF check at all" $ let + cookieCfgNoXsrf = cookieCfg { cookieXsrfSetting = Nothing } + in around (testWithApplication . return $ appWithCookie cookieOnlyApi cookieCfgNoXsrf) $ do + + it "succeeds with no XSRF header or cookie for GET" $ \port -> property + $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts <- addJwtToCookie cookieCfgNoXsrf jwt + resp <- getWith opts (url port) + resp ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + it "succeeds with no XSRF header or cookie for POST" $ \port -> property + $ \(user :: User) number -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims $ toJSON user) + opts <- addJwtToCookie cookieCfgNoXsrf jwt + resp <- postWith opts (url port) $ toJSON (number :: Int) + resp ^? responseBody . _JSON `shouldBe` Just number + + it "sets and clears the right cookies" $ \port -> property + $ \(user :: User) -> do + let optsFromResp resp = defaults + & cookies .~ Just (resp ^. responseCookieJar) -- real cookie jars aren't updated by being replaced + + resp <- postWith defaults (url port ++ "/login") user + (resp ^. responseCookieJar) `shouldMatchCookieNames` + [ sessionCookieName cookieCfg + , "NO-XSRF-TOKEN" + ] + let loggedInOpts = optsFromResp resp + + resp <- getWith (loggedInOpts) (url port) + resp ^? responseBody . _JSON `shouldBe` Just (length $ name user) + + resp <- getWith loggedInOpts (url port ++ "/logout") + (resp ^. responseCookieJar) `shouldMatchCookieNameValues` + [ (sessionCookieName cookieCfg, "value") + , ("NO-XSRF-TOKEN", "") + ] + + -- assert cookies were expired + now <- getCurrentTime + let assertCookie c = now >= cookie_expiry_time c + all assertCookie (destroyCookieJar (resp ^. responseCookieJar)) `shouldBe` True + + let loggedOutOpts = optsFromResp resp + + getWith loggedOutOpts (url port) `shouldHTTPErrorWith` status401 + +-- }}} +------------------------------------------------------------------------------ +-- * JWT Auth {{{ + +jwtAuthSpec :: Spec +jwtAuthSpec + = describe "The JWT combinator" + $ around (testWithApplication . return $ app jwtOnlyApi) $ do + + it "fails if 'aud' does not match predicate" $ \port -> property $ + \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims (toJSON user) & claimAud .~ Just (Audience ["boo"])) + opts <- addJwtToHeader (jwt >>= (return . encodeCompact)) + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "succeeds if 'aud' does match predicate" $ \port -> property $ + \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims (toJSON user) & claimAud .~ Just (Audience ["anythingElse"])) + opts <- addJwtToHeader (jwt >>= (return . encodeCompact)) + resp <- getWith opts (url port) + resp ^. responseStatus `shouldBe` status200 + + it "fails if 'nbf' is set to a future date" $ \port -> property $ + \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims (toJSON user) & claimNbf .~ Just (NumericDate future)) + opts <- addJwtToHeader (jwt >>= (return . encodeCompact)) + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "fails if 'exp' is set to a past date" $ \port -> property $ + \(user :: User) -> do + jwt <- makeJWT user jwtCfg (Just past) + opts <- addJwtToHeader jwt + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "succeeds if 'exp' is set to a future date" $ \port -> property $ + \(user :: User) -> do + jwt <- makeJWT user jwtCfg (Just future) + opts <- addJwtToHeader jwt + resp <- getWith opts (url port) + resp ^. responseStatus `shouldBe` status200 + + it "fails if JWT is not signed" $ \port -> property $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), None)) + (claims $ toJSON user) + opts <- addJwtToHeader (jwt >>= (return . encodeCompact)) + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "fails if JWT does not use expected algorithm" $ const $ + pendingWith "Need https://github.com/frasertweedale/hs-jose/issues/19" + + it "fails if data is not valid JSON" $ \port -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) (claims "{{") + opts <- addJwtToHeader (jwt >>= (return .encodeCompact)) + getWith opts (url port) `shouldHTTPErrorWith` status401 + + it "suceeds as wreq's oauth2Bearer" $ \port -> property $ \(user :: User) -> do + jwt <- createJWT theKey (newJWSHeader ((), HS256)) + (claims $ toJSON user) + resp <- case jwt >>= (return . encodeCompact) of + Left (e :: Error) -> fail $ show e + Right v -> getWith (defaults & auth ?~ oauth2Bearer (BSL.toStrict v)) (url port) + resp ^. responseStatus `shouldBe` status200 + +-- }}} +------------------------------------------------------------------------------ +-- * Basic Auth {{{ + +basicAuthSpec :: Spec +basicAuthSpec = describe "The BasicAuth combinator" + $ around (testWithApplication . return $ app basicAuthApi) $ do + + it "succeeds with the correct password and username" $ \port -> do + resp <- getWith (defaults & auth ?~ basicAuth "ali" "Open sesame") (url port) + resp ^. responseStatus `shouldBe` status200 + + it "fails with non-existent user" $ \port -> do + getWith (defaults & auth ?~ basicAuth "thief" "Open sesame") (url port) + `shouldHTTPErrorWith` status401 + + it "fails with incorrect password" $ \port -> do + getWith (defaults & auth ?~ basicAuth "ali" "phatic") (url port) + `shouldHTTPErrorWith` status401 + + it "fails with no auth header" $ \port -> do + get (url port) `shouldHTTPErrorWith` status401 + +-- }}} +------------------------------------------------------------------------------ +-- * ThrowAll {{{ + +throwAllSpec :: Spec +throwAllSpec = describe "throwAll" $ do + + it "works for plain values" $ do + let t :: Either ServerError Int :<|> Either ServerError Bool :<|> Either ServerError String + t = throwAll err401 + t `shouldBe` throwError err401 :<|> throwError err401 :<|> throwError err401 + + it "works for function types" $ property $ \i -> do + let t :: Int -> (Either ServerError Bool :<|> Either ServerError String) + t = throwAll err401 + expected _ = throwError err401 :<|> throwError err401 + t i `shouldBe` expected i + +-- }}} +------------------------------------------------------------------------------ +-- * API and Server {{{ + +type API auths + = Auth auths User :> + ( Get '[JSON] Int + :<|> ReqBody '[JSON] Int :> Post '[JSON] Int + :<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int) +#if MIN_VERSION_servant_server(0,15,0) + :<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString) +#endif + :<|> "raw" :> Raw + ) + :<|> "login" :> ReqBody '[JSON] User :> Post '[JSON] (Headers '[ Header "Set-Cookie" SetCookie + , Header "Set-Cookie" SetCookie ] NoContent) + :<|> "logout" :> Get '[JSON] (Headers '[ Header "Set-Cookie" SetCookie + , Header "Set-Cookie" SetCookie ] NoContent) + +jwtOnlyApi :: Proxy (API '[Servant.Auth.Server.JWT]) +jwtOnlyApi = Proxy + +cookieOnlyApi :: Proxy (API '[Cookie]) +cookieOnlyApi = Proxy + +basicAuthApi :: Proxy (API '[BasicAuth]) +basicAuthApi = Proxy + +jwtAndCookieApi :: Proxy (API '[Servant.Auth.Server.JWT, Cookie]) +jwtAndCookieApi = Proxy + +theKey :: JWK +theKey = unsafePerformIO . genJWK $ OctGenParam 256 +{-# NOINLINE theKey #-} + + +cookieCfg :: CookieSettings +cookieCfg = def + { cookieExpires = Just future + , cookieIsSecure = NotSecure + , sessionCookieName = "RuncibleSpoon" + , cookieXsrfSetting = pure $ def + { xsrfCookieName = "TheyDinedOnMince" + , xsrfHeaderName = "AndSlicesOfQuince" + } + } +xsrfField :: (XsrfCookieSettings -> a) -> CookieSettings -> a +xsrfField f = maybe (error "expected XsrfCookieSettings for test") f . cookieXsrfSetting + +jwtCfg :: JWTSettings +jwtCfg = (defaultJWTSettings theKey) { audienceMatches = \x -> + if x == "boo" then DoesNotMatch else Matches } + +instance FromBasicAuthData User where + fromBasicAuthData (BasicAuthData usr pwd) _ + = return $ if usr == "ali" && pwd == "Open sesame" + then Authenticated $ User "ali" "ali@the-thieves-den.com" + else Indefinite + +-- Could be anything, really, but since this is already in the cfg we don't +-- have to add it +type instance BasicAuthCfg = JWK + +appWithCookie :: AreAuths auths '[CookieSettings, JWTSettings, JWK] User + => Proxy (API auths) -> CookieSettings -> Application +appWithCookie api ccfg = serveWithContext api ctx $ server ccfg + where + ctx = ccfg :. jwtCfg :. theKey :. EmptyContext + +-- | Takes a proxy parameter indicating which authentication systems to enable. +app :: AreAuths auths '[CookieSettings, JWTSettings, JWK] User + => Proxy (API auths) -> Application +app api = appWithCookie api cookieCfg + +server :: CookieSettings -> Server (API auths) +server ccfg = + (\authResult -> case authResult of + Authenticated usr -> getInt usr + :<|> postInt usr + :<|> getHeaderInt +#if MIN_VERSION_servant_server(0,15,0) + :<|> return (S.source ["bytestring"]) +#endif + :<|> raw + Indefinite -> throwAll err401 + _ -> throwAll err403 + ) + :<|> getLogin + :<|> getLogout + where + getInt :: User -> Handler Int + getInt usr = return . length $ name usr + + postInt :: User -> Int -> Handler Int + postInt _ = return + + getHeaderInt :: Handler (Headers '[Header "Blah" Int] Int) + getHeaderInt = return $ addHeader 1797 17 + + getLogin :: User -> Handler (Headers '[ Header "Set-Cookie" SetCookie + , Header "Set-Cookie" SetCookie ] NoContent) + getLogin user = do + maybeApplyCookies <- liftIO $ acceptLogin ccfg jwtCfg user + case maybeApplyCookies of + Just applyCookies -> return $ applyCookies NoContent + Nothing -> error "cookies failed to apply" + + getLogout :: Handler (Headers '[ Header "Set-Cookie" SetCookie + , Header "Set-Cookie" SetCookie ] NoContent) + getLogout = return $ clearSession ccfg NoContent + + raw :: Server Raw + raw = +#if MIN_VERSION_servant_server(0,11,0) + Tagged $ +#endif + \_req respond -> + respond $ responseLBS status200 [("hi", "there")] "how are you?" + +-- }}} +------------------------------------------------------------------------------ +-- * 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" + +addJwtToHeader :: Either Error BSL.ByteString -> IO Options +addJwtToHeader jwt = case jwt of + Left e -> fail $ show e + Right v -> return + $ defaults & header "Authorization" .~ ["Bearer " <> BSL.toStrict v] + +createJWT :: JWK -> JWSHeader () -> ClaimsSet -> IO (Either Error Crypto.JWT.SignedJWT) +createJWT k a b = runExceptT $ signClaims k a b + +addJwtToCookie :: ToCompact a => CookieSettings -> Either Error a -> IO Options +addJwtToCookie ccfg jwt = case jwt >>= (return . encodeCompact) of + Left e -> fail $ show e + Right v -> return + $ defaults & header "Cookie" .~ [sessionCookieName ccfg <> "=" <> BSL.toStrict v] + +addCookie :: Options -> BS.ByteString -> Options +addCookie opts cookie' = opts & header "Cookie" %~ \c -> case c of + [h] -> [cookie' <> "; " <> h] + [] -> [cookie'] + _ -> error "expecting single cookie header" + + +shouldHTTPErrorWith :: IO a -> Status -> Expectation +shouldHTTPErrorWith act stat = act `shouldThrow` \e -> case e of +#if MIN_VERSION_http_client(0,5,0) + HCli.HttpExceptionRequest _ (HCli.StatusCodeException resp _) + -> HCli.responseStatus resp == stat +#else + HCli.StatusCodeException x _ _ -> x == stat +#endif + _ -> False + +shouldMatchCookieNames :: HCli.CookieJar -> [BS.ByteString] -> Expectation +shouldMatchCookieNames cj patterns + = fmap cookie_name (destroyCookieJar cj) + `shouldMatchList` patterns + +shouldMatchCookieNameValues :: HCli.CookieJar -> [(BS.ByteString, BS.ByteString)] -> Expectation +shouldMatchCookieNameValues cj patterns + = fmap ((,) <$> cookie_name <*> cookie_value) (destroyCookieJar cj) + `shouldMatchList` patterns + +url :: Int -> String +url port = "http://localhost:" <> show port + +claims :: Value -> ClaimsSet +claims val = emptyClaimsSet & unregisteredClaims . at "dat" .~ Just val +-- }}} +------------------------------------------------------------------------------ +-- * 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 + +instance Postable User where + postPayload user request = return $ request + { HCli.requestBody = HCli.RequestBodyLBS $ encode user + , HCli.requestHeaders = (mk "Content-Type", "application/json") : HCli.requestHeaders request + } + + +-- }}} diff --git a/servant-auth/servant-auth-server/test/Spec.hs b/servant-auth/servant-auth-server/test/Spec.hs new file mode 100644 index 00000000..a824f8c3 --- /dev/null +++ b/servant-auth/servant-auth-server/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/servant-auth/servant-auth-swagger/.ghci b/servant-auth/servant-auth-swagger/.ghci new file mode 100644 index 00000000..ae927ec4 --- /dev/null +++ b/servant-auth/servant-auth-swagger/.ghci @@ -0,0 +1 @@ +:set -isrc -itest -idoctest/ghci-wrapper/src diff --git a/servant-auth/servant-auth-swagger/CHANGELOG.md b/servant-auth/servant-auth-swagger/CHANGELOG.md new file mode 100644 index 00000000..7c14608a --- /dev/null +++ b/servant-auth/servant-auth-swagger/CHANGELOG.md @@ -0,0 +1,24 @@ +# 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.1] - 2020-10-06 + +### Changed + +- Support GHC 8.10 @domenkozar +- Fix build with swagger 2.5.x @domenkozar + +## [0.2.10.0] - 2018-06-18 + +### Added + +- Support for GHC 8.4 by @phadej +- Changelog by @domenkozar +- #93: Add Cookie in SwaggerSpec API by @domenkozar +- #42: Add dummy AllHasSecurity Cookie instance by @sordina diff --git a/servant-auth/servant-auth-swagger/LICENSE b/servant-auth/servant-auth-swagger/LICENSE new file mode 100644 index 00000000..302f74f7 --- /dev/null +++ b/servant-auth/servant-auth-swagger/LICENSE @@ -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. + diff --git a/servant-auth/servant-auth-swagger/Setup.hs b/servant-auth/servant-auth-swagger/Setup.hs new file mode 100644 index 00000000..9a994af6 --- /dev/null +++ b/servant-auth/servant-auth-swagger/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal new file mode 100644 index 00000000..fcc9b43b --- /dev/null +++ b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal @@ -0,0 +1,70 @@ +name: servant-auth-swagger +version: 0.2.10.1 +synopsis: servant-swagger/servant-auth compatibility +description: This package provides instances that allow generating swagger2 schemas from + + APIs that use + @Auth@ combinator. + . + For a quick overview of the usage, see the . +category: Web, Servant, Authentication +homepage: http://github.com/haskell-servant/servant-auth#readme +bug-reports: https://github.com/haskell-servant/servant-auth/issues +author: Julian K. Arni +maintainer: jkarni@gmail.com +copyright: (c) Julian K. Arni +license: BSD3 +license-file: LICENSE +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 +build-type: Simple +cabal-version: >= 1.10 +extra-source-files: + CHANGELOG.md + +source-repository head + type: git + location: https://github.com/haskell-servant/servant-auth + +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.16 + , text >= 1.2.3.0 && < 1.3 + , servant-swagger >= 1.1.5 && < 1.8 + , swagger2 >= 2.2.2 && < 2.7 + , servant >= 0.13 && < 0.19 + , servant-auth == 0.4.* + , lens >= 4.16.1 && < 5.1 + exposed-modules: + Servant.Auth.Swagger + 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.9 + -- dependencies with bounds inherited from the library stanza + build-depends: + base + , text + , servant-swagger + , swagger2 + , servant + , servant-auth + , lens + + -- test dependencies + build-depends: + servant-auth-swagger + , hspec >= 2.5.5 && < 2.9 + , QuickCheck >= 2.11.3 && < 2.15 + other-modules: + Servant.Auth.SwaggerSpec + default-language: Haskell2010 diff --git a/servant-auth/servant-auth-swagger/src/Servant/Auth/Swagger.hs b/servant-auth/servant-auth-swagger/src/Servant/Auth/Swagger.hs new file mode 100644 index 00000000..ec6314ca --- /dev/null +++ b/servant-auth/servant-auth-swagger/src/Servant/Auth/Swagger.hs @@ -0,0 +1,87 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE CPP #-} +module Servant.Auth.Swagger + ( + -- | The purpose of this package is provide the instance for 'servant-auth' + -- combinators needed for 'servant-swagger' documentation generation. + -- + -- Currently only JWT and BasicAuth are supported. + + -- * Re-export + JWT + , BasicAuth + , Auth + + -- * Needed to define instances of @HasSwagger@ + , HasSecurity (..) + ) where + +import Control.Lens ((&), (<>~)) +import Data.Proxy (Proxy (Proxy)) +import Data.Swagger (ApiKeyLocation (..), ApiKeyParams (..), + SecurityRequirement (..), SecurityScheme (..), +#if MIN_VERSION_swagger2(2,6,0) + SecurityDefinitions(..), +#endif + SecuritySchemeType (..), allOperations, security, + securityDefinitions) +import GHC.Exts (fromList) +import Servant.API hiding (BasicAuth) +import Servant.Auth +import Servant.Swagger + +import qualified Data.Text as T + +instance (AllHasSecurity xs, HasSwagger api) => HasSwagger (Auth xs r :> api) where + toSwagger _ + = toSwagger (Proxy :: Proxy api) + & securityDefinitions <>~ mkSec (fromList secs) + & allOperations.security <>~ secReqs + where + secs = securities (Proxy :: Proxy xs) + secReqs = [ SecurityRequirement (fromList [(s,[])]) | (s,_) <- secs] + mkSec = +#if MIN_VERSION_swagger2(2,6,0) + SecurityDefinitions +#else + id +#endif + + +class HasSecurity x where + securityName :: Proxy x -> T.Text + securityScheme :: Proxy x -> SecurityScheme + +instance HasSecurity BasicAuth where + securityName _ = "BasicAuth" + securityScheme _ = SecurityScheme type_ (Just desc) + where + type_ = SecuritySchemeBasic + desc = "Basic access authentication" + +instance HasSecurity JWT where + securityName _ = "JwtSecurity" + securityScheme _ = SecurityScheme type_ (Just desc) + where + type_ = SecuritySchemeApiKey (ApiKeyParams "Authorization" ApiKeyHeader) + desc = "JSON Web Token-based API key" + +class AllHasSecurity (x :: [*]) where + securities :: Proxy x -> [(T.Text,SecurityScheme)] + +instance {-# OVERLAPPABLE #-} (HasSecurity x, AllHasSecurity xs) => AllHasSecurity (x ': xs) where + securities _ = (securityName px, securityScheme px) : securities pxs + where + px :: Proxy x + px = Proxy + pxs :: Proxy xs + pxs = Proxy + +instance {-# OVERLAPPING #-} AllHasSecurity xs => AllHasSecurity (Cookie ': xs) where + securities _ = securities pxs + where + pxs :: Proxy xs + pxs = Proxy + +instance AllHasSecurity '[] where + securities _ = [] diff --git a/servant-auth/servant-auth-swagger/test/Servant/Auth/SwaggerSpec.hs b/servant-auth/servant-auth-swagger/test/Servant/Auth/SwaggerSpec.hs new file mode 100644 index 00000000..1bfda413 --- /dev/null +++ b/servant-auth/servant-auth-swagger/test/Servant/Auth/SwaggerSpec.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE CPP #-} +module Servant.Auth.SwaggerSpec (spec) where + +import Control.Lens +import Data.Proxy +import Servant.API +import Servant.Auth +import Servant.Auth.Swagger +import Data.Swagger +import Servant.Swagger +import Test.Hspec + +spec :: Spec +spec = describe "HasSwagger instance" $ do + + let swag = toSwagger (Proxy :: Proxy API) + + it "adds security definitions at the top level" $ do +#if MIN_VERSION_swagger2(2,6,0) + let (SecurityDefinitions secDefs) = swag ^. securityDefinitions +#else + let secDefs = swag ^. securityDefinitions +#endif + length secDefs `shouldSatisfy` (> 0) + + it "adds security at sub-apis" $ do + swag ^. security `shouldBe` [] + show (swag ^. paths . at "/secure") `shouldContain` "JwtSecurity" + show (swag ^. paths . at "/insecure") `shouldNotContain` "JwtSecurity" + +-- * API + +type API = "secure" :> Auth '[JWT, Cookie] Int :> SecureAPI + :<|> "insecure" :> InsecureAPI + +type SecureAPI = Get '[JSON] Int :<|> ReqBody '[JSON] Int :> Post '[JSON] Int + +type InsecureAPI = SecureAPI diff --git a/servant-auth/servant-auth-swagger/test/Spec.hs b/servant-auth/servant-auth-swagger/test/Spec.hs new file mode 100644 index 00000000..a824f8c3 --- /dev/null +++ b/servant-auth/servant-auth-swagger/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/servant-auth/servant-auth.project b/servant-auth/servant-auth.project new file mode 100644 index 00000000..64ef7535 --- /dev/null +++ b/servant-auth/servant-auth.project @@ -0,0 +1,6 @@ +packages: + servant-auth + servant-auth-client + servant-auth-docs + servant-auth-server + servant-auth-swagger diff --git a/servant-auth/servant-auth/.ghci b/servant-auth/servant-auth/.ghci new file mode 100644 index 00000000..ae927ec4 --- /dev/null +++ b/servant-auth/servant-auth/.ghci @@ -0,0 +1 @@ +:set -isrc -itest -idoctest/ghci-wrapper/src diff --git a/servant-auth/servant-auth/CHANGELOG.md b/servant-auth/servant-auth/CHANGELOG.md new file mode 100644 index 00000000..cb1d5b8f --- /dev/null +++ b/servant-auth/servant-auth/CHANGELOG.md @@ -0,0 +1,20 @@ +# 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.0.0] - 2020-10-06 + +- Support for GHC 8.10 by @domenkozar +- Support servant 0.18 by @domenkozar +- Move `ToJWT/FromJWT` from servant-auth-server + +## [0.3.2.0] - 2018-06-18 + +### Added +- Support for GHC 8.4 by @phadej +- Changelog by @domenkozar diff --git a/servant-auth/servant-auth/LICENSE b/servant-auth/servant-auth/LICENSE new file mode 100644 index 00000000..302f74f7 --- /dev/null +++ b/servant-auth/servant-auth/LICENSE @@ -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. + diff --git a/servant-auth/servant-auth/Setup.hs b/servant-auth/servant-auth/Setup.hs new file mode 100644 index 00000000..9a994af6 --- /dev/null +++ b/servant-auth/servant-auth/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/servant-auth/servant-auth/servant-auth.cabal b/servant-auth/servant-auth/servant-auth.cabal new file mode 100644 index 00000000..b0b03733 --- /dev/null +++ b/servant-auth/servant-auth/servant-auth.cabal @@ -0,0 +1,46 @@ +name: servant-auth +version: 0.4.0.0 +synopsis: Authentication combinators for servant +description: This package provides an @Auth@ combinator for 'servant'. This combinator + allows using different authentication schemes in a straightforward way, + and possibly in conjunction with one another. + . + 'servant-auth' additionally provides concrete authentication schemes, such + as Basic Access Authentication, JSON Web Tokens, and Cookies. + . + For more details on how to use this, see the . +category: Web, Servant, Authentication +homepage: http://github.com/haskell-servant/servant-auth#readme +bug-reports: https://github.com/haskell-servant/servant-auth/issues +author: Julian K. Arni +maintainer: jkarni@gmail.com +copyright: (c) Julian K. Arni +license: BSD3 +license-file: LICENSE +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 +build-type: Simple +cabal-version: >= 1.10 +extra-source-files: + CHANGELOG.md + +source-repository head + type: git + location: https://github.com/haskell-servant/servant-auth + +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.16 + , aeson >= 1.3.1.1 && < 1.6 + , jose >= 0.7.0.0 && < 0.9 + , lens >= 4.16.1 && < 5.1 + , servant >= 0.15 && < 0.19 + , text >= 1.2.3.0 && < 1.3 + , unordered-containers >= 0.2.9.0 && < 0.3 + exposed-modules: + Servant.Auth + Servant.Auth.JWT + default-language: Haskell2010 diff --git a/servant-auth/servant-auth/src/Servant/Auth.hs b/servant-auth/servant-auth/src/Servant/Auth.hs new file mode 100644 index 00000000..1ada6fe2 --- /dev/null +++ b/servant-auth/servant-auth/src/Servant/Auth.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +module Servant.Auth where + +import Data.Proxy (Proxy(..)) +import Servant.API ((:>)) +import Servant.Links (HasLink (..)) + +-- * Authentication + +-- | @Auth [auth1, auth2] val :> api@ represents an API protected *either* by +-- @auth1@ or @auth2@ +data Auth (auths :: [*]) val + +-- | A @HasLink@ instance for @Auth@ +instance HasLink sub => HasLink (Auth (tag :: [*]) value :> sub) where +#if MIN_VERSION_servant(0,14,0) + type MkLink (Auth (tag :: [*]) value :> sub) a = MkLink sub a + toLink toA _ = toLink toA (Proxy :: Proxy sub) +#else + type MkLink (Auth (tag :: [*]) value :> sub) = MkLink sub + toLink _ = toLink (Proxy :: Proxy sub) +#endif + +-- ** Combinators + +-- | A JSON Web Token (JWT) in the the Authorization header: +-- +-- @Authorization: Bearer \@ +-- +-- Note that while the token is signed, it is not encrypted. Therefore do not +-- keep in it any information you would not like the client to know. +-- +-- JWTs are described in IETF's +data JWT + +-- | A cookie. The content cookie itself is a JWT. Another cookie is also used, +-- the contents of which are expected to be send back to the server in a +-- header, for XSRF protection. +data Cookie + + +-- We could use 'servant''s BasicAuth, but then we don't get control over the +-- documentation, and we'd have to polykind everything. (Also, we don't +-- currently depend on servant!) +-- +-- | Basic Auth. +data BasicAuth + +-- | Login via a form. +data FormLogin form diff --git a/servant-auth/servant-auth/src/Servant/Auth/JWT.hs b/servant-auth/servant-auth/src/Servant/Auth/JWT.hs new file mode 100644 index 00000000..f02494ba --- /dev/null +++ b/servant-auth/servant-auth/src/Servant/Auth/JWT.hs @@ -0,0 +1,33 @@ +module Servant.Auth.JWT where + +import Control.Lens ((^.)) +import qualified Crypto.JWT as Jose +import Data.Aeson (FromJSON, Result (..), ToJSON, fromJSON, + toJSON) +import qualified Data.HashMap.Strict as HM +import qualified Data.Text as T + + +-- This should probably also be from ClaimSet +-- +-- | How to decode data from a JWT. +-- +-- The default implementation assumes the data is stored in the unregistered +-- @dat@ claim, and uses the @FromJSON@ instance to decode value from there. +class FromJWT a where + decodeJWT :: Jose.ClaimsSet -> Either T.Text a + default decodeJWT :: FromJSON a => Jose.ClaimsSet -> Either T.Text a + decodeJWT m = case HM.lookup "dat" (m ^. Jose.unregisteredClaims) of + Nothing -> Left "Missing 'dat' claim" + Just v -> case fromJSON v of + Error e -> Left $ T.pack e + Success a -> Right a + +-- | How to encode data from a JWT. +-- +-- The default implementation stores data in the unregistered @dat@ claim, and +-- uses the type's @ToJSON@ instance to encode the data. +class ToJWT a where + encodeJWT :: a -> Jose.ClaimsSet + default encodeJWT :: ToJSON a => a -> Jose.ClaimsSet + encodeJWT a = Jose.addClaim "dat" (toJSON a) Jose.emptyClaimsSet \ No newline at end of file diff --git a/servant-auth/servant-auth/test/Spec.hs b/servant-auth/servant-auth/test/Spec.hs new file mode 100644 index 00000000..a824f8c3 --- /dev/null +++ b/servant-auth/servant-auth/test/Spec.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/servant-auth/stack-lts16.yaml b/servant-auth/stack-lts16.yaml new file mode 100644 index 00000000..448ecd42 --- /dev/null +++ b/servant-auth/stack-lts16.yaml @@ -0,0 +1,7 @@ +resolver: lts-16.31 +packages: +- servant-auth +- servant-auth-server +- servant-auth-client +- servant-auth-docs +- servant-auth-swagger diff --git a/servant-auth/stack-lts17.yaml b/servant-auth/stack-lts17.yaml new file mode 100644 index 00000000..ff877145 --- /dev/null +++ b/servant-auth/stack-lts17.yaml @@ -0,0 +1,7 @@ +resolver: lts-17.5 +packages: +- servant-auth +- servant-auth-server +- servant-auth-client +- servant-auth-docs +- servant-auth-swagger diff --git a/servant-auth/stack.yaml b/servant-auth/stack.yaml new file mode 100644 index 00000000..1d386554 --- /dev/null +++ b/servant-auth/stack.yaml @@ -0,0 +1,7 @@ +resolver: nightly-2021-06-01 +packages: +- servant-auth +- servant-auth-server +- servant-auth-client +- servant-auth-docs +- servant-auth-swagger From 05674e4870f5db0aca5efaa7ff20d1f7cae0b312 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 26 Oct 2021 22:29:04 +0200 Subject: [PATCH 144/156] change servant-auth repo url in cabal files --- .../servant-auth-client/servant-auth-client.cabal | 8 ++++---- servant-auth/servant-auth-docs/servant-auth-docs.cabal | 8 ++++---- .../servant-auth-server/servant-auth-server.cabal | 8 ++++---- .../servant-auth-swagger/servant-auth-swagger.cabal | 8 ++++---- servant-auth/servant-auth/servant-auth.cabal | 8 ++++---- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/servant-auth/servant-auth-client/servant-auth-client.cabal b/servant-auth/servant-auth-client/servant-auth-client.cabal index b385eeb8..edddde2e 100644 --- a/servant-auth/servant-auth-client/servant-auth-client.cabal +++ b/servant-auth/servant-auth-client/servant-auth-client.cabal @@ -6,10 +6,10 @@ description: This package provides instances that allow generating clients fr APIs that use @Auth@ combinator. . - For a quick overview of the usage, see the . + For a quick overview of the usage, see the . category: Web, Servant, Authentication -homepage: http://github.com/haskell-servant/servant-auth#readme -bug-reports: https://github.com/haskell-servant/servant-auth/issues +homepage: http://github.com/haskell-servant/servant/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 @@ -23,7 +23,7 @@ extra-source-files: source-repository head type: git - location: https://github.com/haskell-servant/servant-auth + location: https://github.com/haskell-servant/servant library hs-source-dirs: diff --git a/servant-auth/servant-auth-docs/servant-auth-docs.cabal b/servant-auth/servant-auth-docs/servant-auth-docs.cabal index f00cc575..e2e94965 100644 --- a/servant-auth/servant-auth-docs/servant-auth-docs.cabal +++ b/servant-auth/servant-auth-docs/servant-auth-docs.cabal @@ -6,10 +6,10 @@ description: This package provides instances that allow generating docs from APIs that use @Auth@ combinator. . - For a quick overview of the usage, see the . + For a quick overview of the usage, see the . category: Web, Servant, Authentication -homepage: http://github.com/haskell-servant/servant-auth#readme -bug-reports: https://github.com/haskell-servant/servant-auth/issues +homepage: http://github.com/haskell-servant/servant/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 @@ -27,7 +27,7 @@ custom-setup source-repository head type: git - location: https://github.com/haskell-servant/servant-auth + location: https://github.com/haskell-servant/servant library hs-source-dirs: diff --git a/servant-auth/servant-auth-server/servant-auth-server.cabal b/servant-auth/servant-auth-server/servant-auth-server.cabal index a657e97d..17d4c63f 100644 --- a/servant-auth/servant-auth-server/servant-auth-server.cabal +++ b/servant-auth/servant-auth-server/servant-auth-server.cabal @@ -6,10 +6,10 @@ description: This package provides the required instances for using the @Auth . Both cookie- and token- (REST API) based authentication is provided. . - For a quick overview of the usage, see the . + For a quick overview of the usage, see the . category: Web, Servant, Authentication -homepage: http://github.com/haskell-servant/servant-auth#readme -bug-reports: https://github.com/haskell-servant/servant-auth/issues +homepage: http://github.com/haskell-servant/servant/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 @@ -23,7 +23,7 @@ extra-source-files: source-repository head type: git - location: https://github.com/haskell-servant/servant-auth + location: https://github.com/haskell-servant/servant library hs-source-dirs: diff --git a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal index fcc9b43b..fbfa2b8b 100644 --- a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal +++ b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal @@ -6,10 +6,10 @@ description: This package provides instances that allow generating swagger2 s APIs that use @Auth@ combinator. . - For a quick overview of the usage, see the . + For a quick overview of the usage, see the . category: Web, Servant, Authentication -homepage: http://github.com/haskell-servant/servant-auth#readme -bug-reports: https://github.com/haskell-servant/servant-auth/issues +homepage: http://github.com/haskell-servant/servant/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 @@ -23,7 +23,7 @@ extra-source-files: source-repository head type: git - location: https://github.com/haskell-servant/servant-auth + location: https://github.com/haskell-servant/servant library hs-source-dirs: diff --git a/servant-auth/servant-auth/servant-auth.cabal b/servant-auth/servant-auth/servant-auth.cabal index b0b03733..f560c56d 100644 --- a/servant-auth/servant-auth/servant-auth.cabal +++ b/servant-auth/servant-auth/servant-auth.cabal @@ -8,10 +8,10 @@ description: This package provides an @Auth@ combinator for 'servant'. This c 'servant-auth' additionally provides concrete authentication schemes, such as Basic Access Authentication, JSON Web Tokens, and Cookies. . - For more details on how to use this, see the . + For more details on how to use this, see the . category: Web, Servant, Authentication -homepage: http://github.com/haskell-servant/servant-auth#readme -bug-reports: https://github.com/haskell-servant/servant-auth/issues +homepage: http://github.com/haskell-servant/servant/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 @@ -25,7 +25,7 @@ extra-source-files: source-repository head type: git - location: https://github.com/haskell-servant/servant-auth + location: https://github.com/haskell-servant/servant library hs-source-dirs: From 8e7a775cdd6acc0f6df7482fdde9d7d519c4f456 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Wed, 27 Oct 2021 18:25:15 +0200 Subject: [PATCH 145/156] servant-auth: removing unused files from former repo --- servant-auth/RELEASE.md | 7 ------- servant-auth/servant-auth.project | 6 ------ servant-auth/stack-lts16.yaml | 7 ------- servant-auth/stack-lts17.yaml | 7 ------- servant-auth/stack.yaml | 7 ------- 5 files changed, 34 deletions(-) delete mode 100644 servant-auth/RELEASE.md delete mode 100644 servant-auth/servant-auth.project delete mode 100644 servant-auth/stack-lts16.yaml delete mode 100644 servant-auth/stack-lts17.yaml delete mode 100644 servant-auth/stack.yaml diff --git a/servant-auth/RELEASE.md b/servant-auth/RELEASE.md deleted file mode 100644 index 303e4cf3..00000000 --- a/servant-auth/RELEASE.md +++ /dev/null @@ -1,7 +0,0 @@ -- update changelog -- bump version in cabal file -- stack sdist servant-auth-server -- git commit -m "v0.4.0.0" -- git tag -s servant-auth-server-0.4.0.0 -- git push --tags -- stack upload servant-auth-server diff --git a/servant-auth/servant-auth.project b/servant-auth/servant-auth.project deleted file mode 100644 index 64ef7535..00000000 --- a/servant-auth/servant-auth.project +++ /dev/null @@ -1,6 +0,0 @@ -packages: - servant-auth - servant-auth-client - servant-auth-docs - servant-auth-server - servant-auth-swagger diff --git a/servant-auth/stack-lts16.yaml b/servant-auth/stack-lts16.yaml deleted file mode 100644 index 448ecd42..00000000 --- a/servant-auth/stack-lts16.yaml +++ /dev/null @@ -1,7 +0,0 @@ -resolver: lts-16.31 -packages: -- servant-auth -- servant-auth-server -- servant-auth-client -- servant-auth-docs -- servant-auth-swagger diff --git a/servant-auth/stack-lts17.yaml b/servant-auth/stack-lts17.yaml deleted file mode 100644 index ff877145..00000000 --- a/servant-auth/stack-lts17.yaml +++ /dev/null @@ -1,7 +0,0 @@ -resolver: lts-17.5 -packages: -- servant-auth -- servant-auth-server -- servant-auth-client -- servant-auth-docs -- servant-auth-swagger diff --git a/servant-auth/stack.yaml b/servant-auth/stack.yaml deleted file mode 100644 index 1d386554..00000000 --- a/servant-auth/stack.yaml +++ /dev/null @@ -1,7 +0,0 @@ -resolver: nightly-2021-06-01 -packages: -- servant-auth -- servant-auth-server -- servant-auth-client -- servant-auth-docs -- servant-auth-swagger From 48d22a35b8df272bd55d9b15d77b5c5a33ce5db4 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Wed, 27 Oct 2021 18:28:42 +0200 Subject: [PATCH 146/156] servant-auth: removing CI status in README, Servant attribution now --- servant-auth/servant-auth-server/README.lhs | 2 -- 1 file changed, 2 deletions(-) diff --git a/servant-auth/servant-auth-server/README.lhs b/servant-auth/servant-auth-server/README.lhs index 9dba4acc..27259465 100644 --- a/servant-auth/servant-auth-server/README.lhs +++ b/servant-auth/servant-auth-server/README.lhs @@ -1,7 +1,5 @@ # servant-auth -[![build status](https://img.shields.io/github/workflow/status/haskell-servant/servant-auth/CI/master?style=flat-square&logo=github&label=build%20status)](https://github.com/haskell-servant/servant-auth/actions?query=workflow%3ACI) - These packages provides safe and easy-to-use authentication options for `servant`. The same API can be protected via: - basicauth From 7c012d70d38b6d6a037f0424a8bdfecb0defac8a Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 26 Oct 2021 18:56:55 +0200 Subject: [PATCH 147/156] servant-auth-client: Excluding tests against GHC 9 --- servant-auth/servant-auth-client/servant-auth-client.cabal | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/servant-auth/servant-auth-client/servant-auth-client.cabal b/servant-auth/servant-auth-client/servant-auth-client.cabal index edddde2e..6c8484db 100644 --- a/servant-auth/servant-auth-client/servant-auth-client.cabal +++ b/servant-auth/servant-auth-client/servant-auth-client.cabal @@ -59,7 +59,9 @@ test-suite spec , servant-auth , servant , servant-auth-client - + if impl(ghc >= 9) + buildable: False + -- test dependencies build-depends: hspec >= 2.5.5 && < 2.9 From 95033be30fa4fcb29bac35ff0fe79b524efebce8 Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 26 Oct 2021 18:58:36 +0200 Subject: [PATCH 148/156] server-auth-server: Excluding tests against GHC 9 --- .../servant-auth-server/servant-auth-server.cabal | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/servant-auth/servant-auth-server/servant-auth-server.cabal b/servant-auth/servant-auth-server/servant-auth-server.cabal index 17d4c63f..99499dbb 100644 --- a/servant-auth/servant-auth-server/servant-auth-server.cabal +++ b/servant-auth/servant-auth-server/servant-auth-server.cabal @@ -54,9 +54,7 @@ library , time >= 1.5.0.1 && < 1.10 , unordered-containers >= 0.2.9.0 && < 0.3 , wai >= 3.2.1.2 && < 3.3 - if !impl(ghc >= 8.0) - build-depends: - semigroups >= 0.18.5 && <0.20 + exposed-modules: Servant.Auth.Server Servant.Auth.Server.Internal @@ -89,6 +87,8 @@ test-suite readme default-language: Haskell2010 if impl(ghcjs) buildable: False + if impl(ghc >= 9) + buildable: False test-suite spec type: exitcode-stdio-1.0 @@ -114,6 +114,8 @@ test-suite spec , servant , servant-server , transformers + if impl(ghc >= 9) + buildable: False -- test dependencies build-depends: From e05826a7992f34d67898e03cc33788747dfa482d Mon Sep 17 00:00:00 2001 From: akhesacaro Date: Tue, 26 Oct 2021 19:00:18 +0200 Subject: [PATCH 149/156] servant-auth-swagger: Excluding building against GHC 9.0 (need base > 4.15 but swagger exclude it) --- .../servant-auth-swagger/servant-auth-swagger.cabal | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal index fbfa2b8b..07cd22c0 100644 --- a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal +++ b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal @@ -15,7 +15,7 @@ maintainer: jkarni@gmail.com copyright: (c) Julian K. Arni license: BSD3 license-file: LICENSE -tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 build-type: Simple cabal-version: >= 1.10 extra-source-files: @@ -37,7 +37,9 @@ library , swagger2 >= 2.2.2 && < 2.7 , servant >= 0.13 && < 0.19 , servant-auth == 0.4.* - , lens >= 4.16.1 && < 5.1 + , lens >= 4.16.1 && < 5.1 + if impl(ghc >= 9) + buildable: False exposed-modules: Servant.Auth.Swagger default-language: Haskell2010 @@ -59,7 +61,9 @@ test-suite spec , servant , servant-auth , lens - + if impl(ghc >= 9) + buildable: False + -- test dependencies build-depends: servant-auth-swagger From 551d4936afca7c4c91e250ca150f7c6e005427a2 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sat, 30 Oct 2021 20:43:35 +0200 Subject: [PATCH 150/156] 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 --- .../servant-auth-server/servant-auth-server.cabal | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/servant-auth/servant-auth-server/servant-auth-server.cabal b/servant-auth/servant-auth-server/servant-auth-server.cabal index 99499dbb..fb917e54 100644 --- a/servant-auth/servant-auth-server/servant-auth-server.cabal +++ b/servant-auth/servant-auth-server/servant-auth-server.cabal @@ -33,7 +33,7 @@ library build-depends: base >= 4.10 && < 4.16 , aeson >= 1.3.1.1 && < 1.6 - , base64-bytestring >= 1.0.0.1 && < 1.2 + , base64-bytestring >= 1.0.0.1 && < 1.3 , blaze-builder >= 0.4.1.0 && < 0.5 , bytestring >= 0.10.6.0 && < 0.11 , case-insensitive >= 1.2.0.11 && < 1.3 @@ -55,6 +55,13 @@ library , unordered-containers >= 0.2.9.0 && < 0.3 , wai >= 3.2.1.2 && < 3.3 + if impl(ghc >= 9) + build-depends: + -- base64-bytestring 1.2.1.0 contains important fix for GHC-9, lower versions + -- produce wrong results, thus corrupring JWT via jose package. + -- See: https://github.com/haskell/base64-bytestring/pull/46 + base64-bytestring >= 1.2.1.0 + exposed-modules: Servant.Auth.Server Servant.Auth.Server.Internal @@ -87,8 +94,6 @@ test-suite readme default-language: Haskell2010 if impl(ghcjs) buildable: False - if impl(ghc >= 9) - buildable: False test-suite spec type: exitcode-stdio-1.0 @@ -114,8 +119,6 @@ test-suite spec , servant , servant-server , transformers - if impl(ghc >= 9) - buildable: False -- test dependencies build-depends: From 53b1d9d2b67d227c0573df882b4a49d0c9e06221 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sat, 30 Oct 2021 21:01:01 +0200 Subject: [PATCH 151/156] Enable tests for servant-auth-client Fixes #1474 --- servant-auth/servant-auth-client/servant-auth-client.cabal | 2 -- 1 file changed, 2 deletions(-) diff --git a/servant-auth/servant-auth-client/servant-auth-client.cabal b/servant-auth/servant-auth-client/servant-auth-client.cabal index 6c8484db..edcd030a 100644 --- a/servant-auth/servant-auth-client/servant-auth-client.cabal +++ b/servant-auth/servant-auth-client/servant-auth-client.cabal @@ -59,8 +59,6 @@ test-suite spec , servant-auth , servant , servant-auth-client - if impl(ghc >= 9) - buildable: False -- test dependencies build-depends: From e2e9ce05965ee4e009170a0cb7e186b3ed034e86 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sat, 30 Oct 2021 21:26:44 +0200 Subject: [PATCH 152/156] Enable servant-auth cookbook --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index e25a87bb..04b29bd5 100644 --- a/cabal.project +++ b/cabal.project @@ -36,7 +36,7 @@ packages: doc/cookbook/generic doc/cookbook/hoist-server-with-context doc/cookbook/https - -- doc/cookbook/jwt-and-basic-auth/ + doc/cookbook/jwt-and-basic-auth doc/cookbook/pagination -- doc/cookbook/sentry -- Commented out because servant-quickcheck currently doesn't build. From ca6774d797496f84f5f8c5eda66239a1efc13388 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sat, 30 Oct 2021 21:29:17 +0200 Subject: [PATCH 153/156] Update servant-auth cookbook deps --- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index 55632e90..50ddcb91 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -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 From fea40bd0fcf90a775fe55a24e847ddf870777313 Mon Sep 17 00:00:00 2001 From: "Joseph C. Sible" Date: Sat, 30 Oct 2021 23:26:21 -0400 Subject: [PATCH 154/156] 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. --- changelog.d/1477 | 9 +++++++++ servant/src/Servant/API/ContentTypes.hs | 1 + 2 files changed, 10 insertions(+) create mode 100644 changelog.d/1477 diff --git a/changelog.d/1477 b/changelog.d/1477 new file mode 100644 index 00000000..52432378 --- /dev/null +++ b/changelog.d/1477 @@ -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. + +} diff --git a/servant/src/Servant/API/ContentTypes.hs b/servant/src/Servant/API/ContentTypes.hs index 1706a047..10e8d896 100644 --- a/servant/src/Servant/API/ContentTypes.hs +++ b/servant/src/Servant/API/ContentTypes.hs @@ -2,6 +2,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} From 70f6c4952473b54b841bcd39185e62b82ec5eb22 Mon Sep 17 00:00:00 2001 From: Maxim Koltsov Date: Sun, 31 Oct 2021 12:36:57 +0100 Subject: [PATCH 155/156] 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 --- servant-server/src/Servant/Server/Internal/ServerError.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servant-server/src/Servant/Server/Internal/ServerError.hs b/servant-server/src/Servant/Server/Internal/ServerError.hs index a22e953b..5b5d56e2 100644 --- a/servant-server/src/Servant/Server/Internal/ServerError.hs +++ b/servant-server/src/Servant/Server/Internal/ServerError.hs @@ -187,7 +187,7 @@ err403 = ServerError { errHTTPCode = 403 -- Example: -- -- > failingHandler :: Handler () --- > failingHandler = throwError $ err404 { errBody = "(╯°□°)╯︵ ┻━┻)." } +-- > failingHandler = throwError $ err404 { errBody = "Are you lost?" } -- err404 :: ServerError err404 = ServerError { errHTTPCode = 404 From 4e4ad495efc50aed5af57a9bcf6a426992294477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Choutri?= Date: Sun, 31 Oct 2021 22:37:56 +0100 Subject: [PATCH 156/156] Change the license value to a valid SPDX identifier --- doc/cookbook/basic-auth/basic-auth.cabal | 2 +- doc/cookbook/basic-streaming/basic-streaming.cabal | 2 +- doc/cookbook/curl-mock/curl-mock.cabal | 2 +- doc/cookbook/custom-errors/custom-errors.cabal | 2 +- doc/cookbook/db-mysql-basics/mysql-basics.cabal | 2 +- doc/cookbook/db-postgres-pool/db-postgres-pool.cabal | 2 +- doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal | 2 +- doc/cookbook/file-upload/file-upload.cabal | 2 +- doc/cookbook/generic/generic.cabal | 2 +- .../hoist-server-with-context/hoist-server-with-context.cabal | 2 +- doc/cookbook/https/https.cabal | 2 +- doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal | 2 +- doc/cookbook/open-id-connect/OpenIdConnect.cabal | 2 +- doc/cookbook/pagination/pagination.cabal | 2 +- doc/cookbook/sentry/sentry.cabal | 2 +- doc/cookbook/structuring-apis/structuring-apis.cabal | 2 +- doc/cookbook/testing/testing.cabal | 2 +- doc/cookbook/using-custom-monad/using-custom-monad.cabal | 2 +- doc/cookbook/using-free-client/using-free-client.cabal | 2 +- doc/cookbook/uverb/uverb.cabal | 2 +- doc/tutorial/tutorial.cabal | 2 +- servant-auth/servant-auth-client/servant-auth-client.cabal | 2 +- servant-auth/servant-auth-docs/servant-auth-docs.cabal | 2 +- servant-auth/servant-auth-server/servant-auth-server.cabal | 2 +- servant-auth/servant-auth-swagger/servant-auth-swagger.cabal | 2 +- servant-auth/servant-auth/servant-auth.cabal | 2 +- servant-client-core/servant-client-core.cabal | 2 +- servant-client-ghcjs/servant-client-ghcjs.cabal | 2 +- servant-client/servant-client.cabal | 2 +- servant-conduit/servant-conduit.cabal | 2 +- servant-docs/servant-docs.cabal | 2 +- servant-foreign/servant-foreign.cabal | 2 +- servant-http-streams/servant-http-streams.cabal | 2 +- servant-machines/servant-machines.cabal | 2 +- servant-pipes/servant-pipes.cabal | 2 +- servant-server/servant-server.cabal | 2 +- servant/servant.cabal | 2 +- 37 files changed, 37 insertions(+), 37 deletions(-) diff --git a/doc/cookbook/basic-auth/basic-auth.cabal b/doc/cookbook/basic-auth/basic-auth.cabal index a8551ca3..f46693c0 100644 --- a/doc/cookbook/basic-auth/basic-auth.cabal +++ b/doc/cookbook/basic-auth/basic-auth.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/basic-streaming/basic-streaming.cabal b/doc/cookbook/basic-streaming/basic-streaming.cabal index 07c1e92c..d90ccf24 100644 --- a/doc/cookbook/basic-streaming/basic-streaming.cabal +++ b/doc/cookbook/basic-streaming/basic-streaming.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/curl-mock/curl-mock.cabal b/doc/cookbook/curl-mock/curl-mock.cabal index 0bc9f9ce..741a72f2 100644 --- a/doc/cookbook/curl-mock/curl-mock.cabal +++ b/doc/cookbook/curl-mock/curl-mock.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/custom-errors/custom-errors.cabal b/doc/cookbook/custom-errors/custom-errors.cabal index 972f505e..6677bf59 100644 --- a/doc/cookbook/custom-errors/custom-errors.cabal +++ b/doc/cookbook/custom-errors/custom-errors.cabal @@ -2,7 +2,7 @@ name: cookbook-custom-errors version: 0.1 synopsis: Return custom error messages from combinators homepage: http://docs.servant.dev -license: BSD3 +license: BSD-3-Clause license-file: ../../../servant/LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/doc/cookbook/db-mysql-basics/mysql-basics.cabal b/doc/cookbook/db-mysql-basics/mysql-basics.cabal index 98c07768..62097b15 100644 --- a/doc/cookbook/db-mysql-basics/mysql-basics.cabal +++ b/doc/cookbook/db-mysql-basics/mysql-basics.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal index 9f4245de..d500ed40 100644 --- a/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal +++ b/doc/cookbook/db-postgres-pool/db-postgres-pool.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal index 92b84a8b..e4e13def 100644 --- a/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal +++ b/doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/file-upload/file-upload.cabal b/doc/cookbook/file-upload/file-upload.cabal index 76d54550..3d67687c 100644 --- a/doc/cookbook/file-upload/file-upload.cabal +++ b/doc/cookbook/file-upload/file-upload.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/generic/generic.cabal b/doc/cookbook/generic/generic.cabal index 2a8b93cf..4b089c4e 100644 --- a/doc/cookbook/generic/generic.cabal +++ b/doc/cookbook/generic/generic.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal index a73a80ce..ea734e89 100644 --- a/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal +++ b/doc/cookbook/hoist-server-with-context/hoist-server-with-context.cabal @@ -4,7 +4,7 @@ synopsis: JWT and basic access authentication with a Custom Monad coo 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 diff --git a/doc/cookbook/https/https.cabal b/doc/cookbook/https/https.cabal index 12e33853..045b4888 100644 --- a/doc/cookbook/https/https.cabal +++ b/doc/cookbook/https/https.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal index 50ddcb91..53d4c650 100644 --- a/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal +++ b/doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal @@ -4,7 +4,7 @@ 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 diff --git a/doc/cookbook/open-id-connect/OpenIdConnect.cabal b/doc/cookbook/open-id-connect/OpenIdConnect.cabal index a7c3d35a..c156f8d7 100644 --- a/doc/cookbook/open-id-connect/OpenIdConnect.cabal +++ b/doc/cookbook/open-id-connect/OpenIdConnect.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/pagination/pagination.cabal b/doc/cookbook/pagination/pagination.cabal index 697c8075..18c3b4a3 100644 --- a/doc/cookbook/pagination/pagination.cabal +++ b/doc/cookbook/pagination/pagination.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/sentry/sentry.cabal b/doc/cookbook/sentry/sentry.cabal index 510606c3..282062b9 100644 --- a/doc/cookbook/sentry/sentry.cabal +++ b/doc/cookbook/sentry/sentry.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/structuring-apis/structuring-apis.cabal b/doc/cookbook/structuring-apis/structuring-apis.cabal index 9d1ea4bb..323fa7f1 100644 --- a/doc/cookbook/structuring-apis/structuring-apis.cabal +++ b/doc/cookbook/structuring-apis/structuring-apis.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/testing/testing.cabal b/doc/cookbook/testing/testing.cabal index e7ba158b..56067a8c 100644 --- a/doc/cookbook/testing/testing.cabal +++ b/doc/cookbook/testing/testing.cabal @@ -3,7 +3,7 @@ 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 diff --git a/doc/cookbook/using-custom-monad/using-custom-monad.cabal b/doc/cookbook/using-custom-monad/using-custom-monad.cabal index 5bfa3629..8cac3fc4 100644 --- a/doc/cookbook/using-custom-monad/using-custom-monad.cabal +++ b/doc/cookbook/using-custom-monad/using-custom-monad.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/using-free-client/using-free-client.cabal b/doc/cookbook/using-free-client/using-free-client.cabal index b02ba118..02179703 100644 --- a/doc/cookbook/using-free-client/using-free-client.cabal +++ b/doc/cookbook/using-free-client/using-free-client.cabal @@ -2,7 +2,7 @@ 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 diff --git a/doc/cookbook/uverb/uverb.cabal b/doc/cookbook/uverb/uverb.cabal index 176983fb..80dc1b06 100644 --- a/doc/cookbook/uverb/uverb.cabal +++ b/doc/cookbook/uverb/uverb.cabal @@ -3,7 +3,7 @@ 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: BSD3 +license: BSD-3-Clause license-file: ../../../servant/LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/doc/tutorial/tutorial.cabal b/doc/tutorial/tutorial.cabal index 9d59c825..a2844e9e 100644 --- a/doc/tutorial/tutorial.cabal +++ b/doc/tutorial/tutorial.cabal @@ -6,7 +6,7 @@ description: 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 diff --git a/servant-auth/servant-auth-client/servant-auth-client.cabal b/servant-auth/servant-auth-client/servant-auth-client.cabal index edcd030a..20e33af6 100644 --- a/servant-auth/servant-auth-client/servant-auth-client.cabal +++ b/servant-auth/servant-auth-client/servant-auth-client.cabal @@ -13,7 +13,7 @@ bug-reports: https://github.com/haskell-servant/servant/issues author: Julian K. Arni maintainer: jkarni@gmail.com copyright: (c) Julian K. Arni -license: BSD3 +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 diff --git a/servant-auth/servant-auth-docs/servant-auth-docs.cabal b/servant-auth/servant-auth-docs/servant-auth-docs.cabal index e2e94965..10453fc0 100644 --- a/servant-auth/servant-auth-docs/servant-auth-docs.cabal +++ b/servant-auth/servant-auth-docs/servant-auth-docs.cabal @@ -13,7 +13,7 @@ bug-reports: https://github.com/haskell-servant/servant/issues author: Julian K. Arni maintainer: jkarni@gmail.com copyright: (c) Julian K. Arni -license: BSD3 +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 diff --git a/servant-auth/servant-auth-server/servant-auth-server.cabal b/servant-auth/servant-auth-server/servant-auth-server.cabal index fb917e54..a58e5364 100644 --- a/servant-auth/servant-auth-server/servant-auth-server.cabal +++ b/servant-auth/servant-auth-server/servant-auth-server.cabal @@ -13,7 +13,7 @@ bug-reports: https://github.com/haskell-servant/servant/issues author: Julian K. Arni maintainer: jkarni@gmail.com copyright: (c) Julian K. Arni -license: BSD3 +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 diff --git a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal index 07cd22c0..840a7591 100644 --- a/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal +++ b/servant-auth/servant-auth-swagger/servant-auth-swagger.cabal @@ -13,7 +13,7 @@ bug-reports: https://github.com/haskell-servant/servant/issues author: Julian K. Arni maintainer: jkarni@gmail.com copyright: (c) Julian K. Arni -license: BSD3 +license: BSD-3-Clause license-file: LICENSE tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 build-type: Simple diff --git a/servant-auth/servant-auth/servant-auth.cabal b/servant-auth/servant-auth/servant-auth.cabal index f560c56d..61b3a6a4 100644 --- a/servant-auth/servant-auth/servant-auth.cabal +++ b/servant-auth/servant-auth/servant-auth.cabal @@ -15,7 +15,7 @@ bug-reports: https://github.com/haskell-servant/servant/issues author: Julian K. Arni maintainer: jkarni@gmail.com copyright: (c) Julian K. Arni -license: BSD3 +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 diff --git a/servant-client-core/servant-client-core.cabal b/servant-client-core/servant-client-core.cabal index e4bc3ba7..3d630110 100644 --- a/servant-client-core/servant-client-core.cabal +++ b/servant-client-core/servant-client-core.cabal @@ -10,7 +10,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-client-ghcjs/servant-client-ghcjs.cabal b/servant-client-ghcjs/servant-client-ghcjs.cabal index 639fc7fc..2a2a68ad 100644 --- a/servant-client-ghcjs/servant-client-ghcjs.cabal +++ b/servant-client-ghcjs/servant-client-ghcjs.cabal @@ -15,7 +15,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-client/servant-client.cabal b/servant-client/servant-client.cabal index 481dbd0a..3ca4c88a 100644 --- a/servant-client/servant-client.cabal +++ b/servant-client/servant-client.cabal @@ -14,7 +14,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-conduit/servant-conduit.cabal b/servant-conduit/servant-conduit.cabal index 2958e47d..ccb94c9e 100644 --- a/servant-conduit/servant-conduit.cabal +++ b/servant-conduit/servant-conduit.cabal @@ -10,7 +10,7 @@ description: Servant Stream support for conduit. homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-docs/servant-docs.cabal b/servant-docs/servant-docs.cabal index f0d7949d..64829f3e 100644 --- a/servant-docs/servant-docs.cabal +++ b/servant-docs/servant-docs.cabal @@ -13,7 +13,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-foreign/servant-foreign.cabal b/servant-foreign/servant-foreign.cabal index b9c01048..d438ff38 100644 --- a/servant-foreign/servant-foreign.cabal +++ b/servant-foreign/servant-foreign.cabal @@ -15,7 +15,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-http-streams/servant-http-streams.cabal b/servant-http-streams/servant-http-streams.cabal index b7798f1c..2f86935c 100644 --- a/servant-http-streams/servant-http-streams.cabal +++ b/servant-http-streams/servant-http-streams.cabal @@ -14,7 +14,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-machines/servant-machines.cabal b/servant-machines/servant-machines.cabal index 43bc7b46..cdf55fdf 100644 --- a/servant-machines/servant-machines.cabal +++ b/servant-machines/servant-machines.cabal @@ -10,7 +10,7 @@ description: Servant Stream support for machines. homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-pipes/servant-pipes.cabal b/servant-pipes/servant-pipes.cabal index 599268f1..4ed0ff02 100644 --- a/servant-pipes/servant-pipes.cabal +++ b/servant-pipes/servant-pipes.cabal @@ -10,7 +10,7 @@ description: Servant Stream support for pipes. homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant-server/servant-server.cabal b/servant-server/servant-server.cabal index 0b3736e8..86e00d31 100644 --- a/servant-server/servant-server.cabal +++ b/servant-server/servant-server.cabal @@ -17,7 +17,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com diff --git a/servant/servant.cabal b/servant/servant.cabal index 7c7af0a1..41ea5792 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -13,7 +13,7 @@ description: homepage: http://docs.servant.dev/ bug-reports: http://github.com/haskell-servant/servant/issues -license: BSD3 +license: BSD-3-Clause license-file: LICENSE author: Servant Contributors maintainer: haskell-servant-maintainers@googlegroups.com