on 0612d2a help sign in
53 files changed+1710−1223
since splitsnapshots
unifiedsplit
.travis.yml+36−38 cabal.make-travis-yml+2−2 cabal.project+5−7 doc/Makefile+10−206 doc/conf.py+54−175 doc/cookbook/basic-auth/basic-auth.cabal+7−1 doc/cookbook/db-postgres-pool/db-postgres-pool.cabal+7−1 doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal+7−1 doc/cookbook/file-upload/file-upload.cabal+7−1 doc/cookbook/generic/Generic.lhs+106 doc/cookbook/generic/generic.cabal+31 doc/cookbook/https/https.cabal+7−1 doc/cookbook/index.rst+1 doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal+7−1 doc/cookbook/pagination/pagination.cabal+8−2 doc/cookbook/structuring-apis/structuring-apis.cabal+7−1 doc/cookbook/using-custom-monad/using-custom-monad.cabal+7−1 doc/index.rst+11−9 doc/make.bat+36 doc/introduction.rst → doc/principles.rst+2−2 doc/req.txt+25 doc/requirements.txt+2−24 doc/setup.py+11 doc/tutorial/Javascript.lhs+1−2 doc/tutorial/index.rst+21−2 doc/tutorial/tutorial.cabal+5−4 servant-client-core/CHANGELOG.md+50−2 servant-client-core/servant-client-core.cabal+23−19 servant-client-core/src/Servant/Client/Generic.hs+51 servant-client-ghcjs/servant-client-ghcjs.cabal+3−3 servant-client/CHANGELOG.md+37−2 servant-client/servant-client.cabal+28−29 servant-client/test/Servant/StreamSpec.hs+2 servant-docs/servant-docs.cabal+10−6 servant-foreign/servant-foreign.cabal+8−4 servant-server/CHANGELOG.md+56 servant-server/servant-server.cabal+40−38 servant-server/src/Servant.hs+2−2 servant-server/src/Servant/Server/Generic.hs+52 servant-server/src/Servant/Server/Internal/Context.hs+1−1 servant-server/src/Servant/Server/StaticFiles.hs+92 servant-server/src/Servant/Utils/StaticFiles.hs+4−84 servant-server/test/Servant/Utils/StaticFilesSpec.hs → servant-server/test/Servant/Server/StaticFilesSpec.hs+25−16 servant/CHANGELOG.md+27−5 servant/servant.cabal+34−25 servant/src/Servant/API.hs+3−3 servant/src/Servant/API/Generic.hs+146 servant/src/Servant/API/Verbs.hs+1−1 servant/src/Servant/Links.hs+573 servant/src/Servant/Utils/Links.hs+4−485 servant/test/Servant/Utils/LinksSpec.hs → servant/test/Servant/LinksSpec.hs+3−3 setup.py+11 stack.yaml+1−14
.travis.yml+36−38
@@ -2,7 +2,7 @@
22#
33# runghc make_travis_yml_2.hs '--config=cabal.make-travis-yml' '--output=.travis.yml' 'cabal.project'
44#
5−# For more information, see https://github.com/hvr/multi-ghc-travis
5+# For more information, see https://github.com/haskell-CI/haskell-ci
66#
77language: c
88sudo: false
@@ -13,7 +13,7 @@ git:
1313branches:
1414 only:
1515 - master
16− - release-0.12
16+ - release-0.14
1717
1818cache:
1919 directories:
@@ -33,21 +33,24 @@ before_cache:
3333
3434matrix:
3535 include:
36− - compiler: "ghc-8.4.3"
36+ - compiler: "ghc-8.6.1"
3737 # env: TEST=--disable-tests BENCH=--disable-benchmarks
38− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
38+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.1], sources: [hvr-ghc]}}
39+ - compiler: "ghc-8.4.4"
40+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
41+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}
3942 - compiler: "ghc-8.2.2"
4043 # env: TEST=--disable-tests BENCH=--disable-benchmarks
41− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
44+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
4245 - compiler: "ghc-8.0.2"
4346 # env: TEST=--disable-tests BENCH=--disable-benchmarks
44− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}
47+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}
4548 - compiler: "ghc-7.10.3"
4649 # env: TEST=--disable-tests BENCH=--disable-benchmarks
47− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}
50+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}
4851 - compiler: "ghc-7.8.4"
4952 # env: TEST=--disable-tests BENCH=--disable-benchmarks
50− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}
53+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}
5154
5255before_install:
5356 - HC=${CC}
@@ -65,17 +68,21 @@ install:
6568 - BENCH=${BENCH---enable-benchmarks}
6669 - TEST=${TEST---enable-tests}
6770 - HADDOCK=${HADDOCK-true}
68− - INSTALLED=${INSTALLED-true}
71+ - UNCONSTRAINED=${UNCONSTRAINED-true}
72+ - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
6973 - GHCHEAD=${GHCHEAD-false}
7074 - travis_retry cabal update -v
7175 - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
7276 - rm -fv cabal.project cabal.project.local
7377 - "if [ $HCNUMVER -ge 70800 ]; then sed -i.bak 's/-- ghc-options:.*/ghc-options: -j2/' ${HOME}/.cabal/config; fi"
7478 - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
75− - "printf 'packages: \"servant\" \"servant-client\" \"servant-client-core\" \"servant-docs\" \"servant-foreign\" \"servant-server\" \"doc/tutorial\" \"doc/cookbook/basic-auth\" \"doc/cookbook/db-postgres-pool\" \"doc/cookbook/db-sqlite-simple\" \"doc/cookbook/https\" \"doc/cookbook/pagination\" \"doc/cookbook/structuring-apis\" \"doc/cookbook/using-custom-monad\"\\n' > cabal.project"
76− - "echo 'constraints: foundation >=0.0.14,memory <0.14.12 || >0.14.12' >> cabal.project"
77− - "echo 'allow-newer: servant-auth-server:http-types,servant-auth-server:servant-server, http-media:base' >> cabal.project"
78− - cat cabal.project
79+ - "printf 'packages: \"servant\" \"servant-client\" \"servant-client-core\" \"servant-docs\" \"servant-foreign\" \"servant-server\" \"doc/tutorial\" \"doc/cookbook/basic-auth\" \"doc/cookbook/db-postgres-pool\" \"doc/cookbook/db-sqlite-simple\" \"doc/cookbook/file-upload\" \"doc/cookbook/generic\" \"doc/cookbook/https\" \"doc/cookbook/structuring-apis\" \"doc/cookbook/using-custom-monad\"\\n' > cabal.project"
80+ - "echo 'constraints: foundation >=0.0.14,memory <0.14.12 || >0.14.12, containers installed' >> cabal.project"
81+ - "echo 'allow-newer: servant-auth-server:http-types,servant-auth-server:servant-server, servant-js:base,servant-pagination:servant,servant-pagination:servant-server' >> cabal.project"
82+ - touch cabal.project.local
83+ - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- servant | grep -vw -- servant-client | grep -vw -- servant-client-core | grep -vw -- servant-docs | grep -vw -- servant-foreign | grep -vw -- servant-server | grep -vw -- tutorial | grep -vw -- cookbook-basic-auth | grep -vw -- cookbook-db-postgres-pool | grep -vw -- cookbook-db-sqlite-simple | grep -vw -- cookbook-file-upload | grep -vw -- cookbook-generic | grep -vw -- cookbook-https | grep -vw -- cookbook-structuring-apis | grep -vw -- cookbook-using-custom-monad | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
84+ - cat cabal.project || true
85+ - cat cabal.project.local || true
7986 - if [ -f "servant/configure.ac" ]; then
8087 (cd "servant" && autoreconf -i);
8188 fi
@@ -106,12 +113,15 @@ install:
106113 - if [ -f "doc/cookbook/db-sqlite-simple/configure.ac" ]; then
107114 (cd "doc/cookbook/db-sqlite-simple" && autoreconf -i);
108115 fi
116+ - if [ -f "doc/cookbook/file-upload/configure.ac" ]; then
117+ (cd "doc/cookbook/file-upload" && autoreconf -i);
118+ fi
119+ - if [ -f "doc/cookbook/generic/configure.ac" ]; then
120+ (cd "doc/cookbook/generic" && autoreconf -i);
121+ fi
109122 - if [ -f "doc/cookbook/https/configure.ac" ]; then
110123 (cd "doc/cookbook/https" && autoreconf -i);
111124 fi
112− - if [ -f "doc/cookbook/pagination/configure.ac" ]; then
113− (cd "doc/cookbook/pagination" && autoreconf -i);
114− fi
115125 - if [ -f "doc/cookbook/structuring-apis/configure.ac" ]; then
116126 (cd "doc/cookbook/structuring-apis" && autoreconf -i);
117127 fi
@@ -119,7 +129,7 @@ install:
119129 (cd "doc/cookbook/using-custom-monad" && autoreconf -i);
120130 fi
121131 - rm -f cabal.project.freeze
122− - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-client-core"/dist "servant-docs"/dist "servant-foreign"/dist "servant-server"/dist "doc/tutorial"/dist "doc/cookbook/basic-auth"/dist "doc/cookbook/db-postgres-pool"/dist "doc/cookbook/db-sqlite-simple"/dist "doc/cookbook/https"/dist "doc/cookbook/pagination"/dist "doc/cookbook/structuring-apis"/dist "doc/cookbook/using-custom-monad"/dist
132+ - rm -rf .ghc.environment.* "servant"/dist "servant-client"/dist "servant-client-core"/dist "servant-docs"/dist "servant-foreign"/dist "servant-server"/dist "doc/tutorial"/dist "doc/cookbook/basic-auth"/dist "doc/cookbook/db-postgres-pool"/dist "doc/cookbook/db-sqlite-simple"/dist "doc/cookbook/file-upload"/dist "doc/cookbook/generic"/dist "doc/cookbook/https"/dist "doc/cookbook/structuring-apis"/dist "doc/cookbook/using-custom-monad"/dist
123133 - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
124134
125135# Here starts the actual work to be performed for the package under test;
@@ -127,32 +137,21 @@ install:
127137script:
128138 # test that source-distributions can be generated
129139 - echo Packaging... && echo -en 'travis_fold:start:sdist\\r'
130− - (cd "servant" && cabal sdist)
131− - (cd "servant-client" && cabal sdist)
132− - (cd "servant-client-core" && cabal sdist)
133− - (cd "servant-docs" && cabal sdist)
134− - (cd "servant-foreign" && cabal sdist)
135− - (cd "servant-server" && cabal sdist)
136− - (cd "doc/tutorial" && cabal sdist)
137− - (cd "doc/cookbook/basic-auth" && cabal sdist)
138− - (cd "doc/cookbook/db-postgres-pool" && cabal sdist)
139− - (cd "doc/cookbook/db-sqlite-simple" && cabal sdist)
140− - (cd "doc/cookbook/https" && cabal sdist)
141− - (cd "doc/cookbook/pagination" && cabal sdist)
142− - (cd "doc/cookbook/structuring-apis" && cabal sdist)
143− - (cd "doc/cookbook/using-custom-monad" && cabal sdist)
140+ - cabal new-sdist all
144141 - echo -en 'travis_fold:end:sdist\\r'
145142 - echo Unpacking... && echo -en 'travis_fold:start:unpack\\r'
146− - mv "servant"/dist/servant-*.tar.gz "servant-client"/dist/servant-client-*.tar.gz "servant-client-core"/dist/servant-client-core-*.tar.gz "servant-docs"/dist/servant-docs-*.tar.gz "servant-foreign"/dist/servant-foreign-*.tar.gz "servant-server"/dist/servant-server-*.tar.gz "doc/tutorial"/dist/tutorial-*.tar.gz "doc/cookbook/basic-auth"/dist/cookbook-basic-auth-*.tar.gz "doc/cookbook/db-postgres-pool"/dist/cookbook-db-postgres-pool-*.tar.gz "doc/cookbook/db-sqlite-simple"/dist/cookbook-db-sqlite-simple-*.tar.gz "doc/cookbook/https"/dist/cookbook-https-*.tar.gz "doc/cookbook/pagination"/dist/cookbook-pagination-*.tar.gz "doc/cookbook/structuring-apis"/dist/cookbook-structuring-apis-*.tar.gz "doc/cookbook/using-custom-monad"/dist/cookbook-using-custom-monad-*.tar.gz ${DISTDIR}/
143+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
147144 - cd ${DISTDIR} || false
148145 - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
149− - "printf 'packages: servant-*/*.cabal servant-client-*/*.cabal servant-client-core-*/*.cabal servant-docs-*/*.cabal servant-foreign-*/*.cabal servant-server-*/*.cabal tutorial-*/*.cabal cookbook-basic-auth-*/*.cabal cookbook-db-postgres-pool-*/*.cabal cookbook-db-sqlite-simple-*/*.cabal cookbook-https-*/*.cabal cookbook-pagination-*/*.cabal cookbook-structuring-apis-*/*.cabal cookbook-using-custom-monad-*/*.cabal\\n' > cabal.project"
150− - "echo 'constraints: foundation >=0.0.14,memory <0.14.12 || >0.14.12' >> cabal.project"
151− - "echo 'allow-newer: servant-auth-server:http-types,servant-auth-server:servant-server, http-media:base' >> cabal.project"
152− - cat cabal.project
146+ - "printf 'packages: servant-*/*.cabal servant-client-*/*.cabal servant-client-core-*/*.cabal servant-docs-*/*.cabal servant-foreign-*/*.cabal servant-server-*/*.cabal tutorial-*/*.cabal cookbook-basic-auth-*/*.cabal cookbook-db-postgres-pool-*/*.cabal cookbook-db-sqlite-simple-*/*.cabal cookbook-file-upload-*/*.cabal cookbook-generic-*/*.cabal cookbook-https-*/*.cabal cookbook-structuring-apis-*/*.cabal cookbook-using-custom-monad-*/*.cabal\\n' > cabal.project"
147+ - "echo 'constraints: foundation >=0.0.14,memory <0.14.12 || >0.14.12, containers installed' >> cabal.project"
148+ - "echo 'allow-newer: servant-auth-server:http-types,servant-auth-server:servant-server, servant-js:base,servant-pagination:servant,servant-pagination:servant-server' >> cabal.project"
149+ - touch cabal.project.local
150+ - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- servant | grep -vw -- servant-client | grep -vw -- servant-client-core | grep -vw -- servant-docs | grep -vw -- servant-foreign | grep -vw -- servant-server | grep -vw -- tutorial | grep -vw -- cookbook-basic-auth | grep -vw -- cookbook-db-postgres-pool | grep -vw -- cookbook-db-sqlite-simple | grep -vw -- cookbook-file-upload | grep -vw -- cookbook-generic | grep -vw -- cookbook-https | grep -vw -- cookbook-structuring-apis | grep -vw -- cookbook-using-custom-monad | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
151+ - cat cabal.project || true
152+ - cat cabal.project.local || true
153153 - echo -en 'travis_fold:end:unpack\\r'
154154
155−
156155 - echo Building with tests and benchmarks... && echo -en 'travis_fold:start:build-everything\\r'
157156 # build & run tests, build benchmarks
158157 - cabal new-build -w ${HC} ${TEST} ${BENCH} all
@@ -161,7 +160,6 @@ script:
161160
162161 - echo Haddock... && echo -en 'travis_fold:start:haddock\\r'
163162 # haddock
164− - rm -rf ./dist-newstyle
165163 - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
166164
167165 - echo -en 'travis_fold:end:haddock\\r'
cabal.make-travis-yml+2−2
@@ -1,12 +1,12 @@
11folds: all-but-test
2−branches: master release-0.12
2+branches: master release-0.14
33
44-- We have inplace packages (servant-js) so we skip installing dependencies in a separate step
55install-dependencies-step: False
66
77-- this speed-ups the build a little, but we have to check these for release
88no-tests-no-benchmarks: False
9−build-with-installed-step: False
9+unconstrained-step: False
1010
1111-- Don't run cabal check, as cookbook examples won't pass it
1212cabal-check: False
cabal.project+5−7
@@ -11,12 +11,11 @@ packages: servant/
1111 doc/cookbook/basic-auth
1212 doc/cookbook/db-postgres-pool
1313 doc/cookbook/db-sqlite-simple
14− -- MkLink changed
15− -- doc/cookbook/file-upload
14+ doc/cookbook/file-upload
15+ doc/cookbook/generic
1616 doc/cookbook/https
17− -- servant-auth-* doesn't support GHC-8.4
1817 -- doc/cookbook/jwt-and-basic-auth
19− doc/cookbook/pagination
18+ -- doc/cookbook/pagination
2019 doc/cookbook/structuring-apis
2120 doc/cookbook/using-custom-monad
2221
@@ -29,10 +28,9 @@ constraints:
2928 foundation >=0.0.14,
3029 memory <0.14.12 || >0.14.12
3130
32−allow-newer:
33− http-media:base
31+constraints: containers installed
3432
3533allow-newer:
36− servant-js:servant,
34+ servant-js:base,
3735 servant-pagination:servant,
3836 servant-pagination:servant-server
doc/Makefile+10−206
@@ -1,216 +1,20 @@
1−# Makefile for Sphinx documentation
1+# Minimal makefile for Sphinx documentation
22#
33
44# You can set these variables from the command line.
55SPHINXOPTS =
66SPHINXBUILD = sphinx-build
7−PAPER =
7+SPHINXPROJ = Servant
8+SOURCEDIR = .
89BUILDDIR = _build
910
10−# User-friendly check for sphinx-build
11−ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12−$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13−endif
14−
15−# Internal variables.
16−PAPEROPT_a4 = -D latex_paper_size=a4
17−PAPEROPT_letter = -D latex_paper_size=letter
18−ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19−# the i18n builder cannot share the environment and doctrees with the others
20−I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21−
22−.PHONY: help
11+# Put it first so that "make" without argument is like "make help".
2312help:
24− @echo "Please use \`make <target>' where <target> is one of"
25− @echo " html to make standalone HTML files"
26− @echo " dirhtml to make HTML files named index.html in directories"
27− @echo " singlehtml to make a single large HTML file"
28− @echo " pickle to make pickle files"
29− @echo " json to make JSON files"
30− @echo " htmlhelp to make HTML files and a HTML help project"
31− @echo " qthelp to make HTML files and a qthelp project"
32− @echo " applehelp to make an Apple Help Book"
33− @echo " devhelp to make HTML files and a Devhelp project"
34− @echo " epub to make an epub"
35− @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36− @echo " latexpdf to make LaTeX files and run them through pdflatex"
37− @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38− @echo " text to make text files"
39− @echo " man to make manual pages"
40− @echo " texinfo to make Texinfo files"
41− @echo " info to make Texinfo files and run them through makeinfo"
42− @echo " gettext to make PO message catalogs"
43− @echo " changes to make an overview of all changed/added/deprecated items"
44− @echo " xml to make Docutils-native XML files"
45− @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46− @echo " linkcheck to check all external links for integrity"
47− @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48− @echo " coverage to run coverage check of the documentation (if enabled)"
49−
50−.PHONY: clean
51−clean:
52− rm -rf $(BUILDDIR)/*
53−
54−.PHONY: html
55−html:
56− $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
57− @echo
58− @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
59−
60−.PHONY: dirhtml
61−dirhtml:
62− $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
63− @echo
64− @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
65−
66−.PHONY: singlehtml
67−singlehtml:
68− $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
69− @echo
70− @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
71−
72−.PHONY: pickle
73−pickle:
74− $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
75− @echo
76− @echo "Build finished; now you can process the pickle files."
77−
78−.PHONY: json
79−json:
80− $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
81− @echo
82− @echo "Build finished; now you can process the JSON files."
83−
84−.PHONY: htmlhelp
85−htmlhelp:
86− $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
87− @echo
88− @echo "Build finished; now you can run HTML Help Workshop with the" \
89− ".hhp project file in $(BUILDDIR)/htmlhelp."
90−
91−.PHONY: qthelp
92−qthelp:
93− $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
94− @echo
95− @echo "Build finished; now you can run "qcollectiongenerator" with the" \
96− ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
97− @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/generics-eot.qhcp"
98− @echo "To view the help file:"
99− @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/generics-eot.qhc"
100−
101−.PHONY: applehelp
102−applehelp:
103− $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
104− @echo
105− @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
106− @echo "N.B. You won't be able to view it unless you put it in" \
107− "~/Library/Documentation/Help or install it in your application" \
108− "bundle."
109−
110−.PHONY: devhelp
111−devhelp:
112− $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
113− @echo
114− @echo "Build finished."
115− @echo "To view the help file:"
116− @echo "# mkdir -p $$HOME/.local/share/devhelp/generics-eot"
117− @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/generics-eot"
118− @echo "# devhelp"
119−
120−.PHONY: epub
121−epub:
122− $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
123− @echo
124− @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
125−
126−.PHONY: latex
127−latex:
128− $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
129− @echo
130− @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
131− @echo "Run \`make' in that directory to run these through (pdf)latex" \
132− "(use \`make latexpdf' here to do that automatically)."
133−
134−.PHONY: latexpdf
135−latexpdf:
136− $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
137− @echo "Running LaTeX files through pdflatex..."
138− $(MAKE) -C $(BUILDDIR)/latex all-pdf
139− @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
140−
141−.PHONY: latexpdfja
142−latexpdfja:
143− $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
144− @echo "Running LaTeX files through platex and dvipdfmx..."
145− $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
146− @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
147−
148−.PHONY: text
149−text:
150− $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
151− @echo
152− @echo "Build finished. The text files are in $(BUILDDIR)/text."
153−
154−.PHONY: man
155−man:
156− $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
157− @echo
158− @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
159−
160−.PHONY: texinfo
161−texinfo:
162− $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
163− @echo
164− @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
165− @echo "Run \`make' in that directory to run these through makeinfo" \
166− "(use \`make info' here to do that automatically)."
167−
168−.PHONY: info
169−info:
170− $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
171− @echo "Running Texinfo files through makeinfo..."
172− make -C $(BUILDDIR)/texinfo info
173− @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
174−
175−.PHONY: gettext
176−gettext:
177− $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
178− @echo
179− @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
180−
181−.PHONY: changes
182−changes:
183− $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
184− @echo
185− @echo "The overview file is in $(BUILDDIR)/changes."
186−
187−.PHONY: linkcheck
188−linkcheck:
189− $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
190− @echo
191− @echo "Link check complete; look for any errors in the above output " \
192− "or in $(BUILDDIR)/linkcheck/output.txt."
193−
194−.PHONY: doctest
195−doctest:
196− $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
197− @echo "Testing of doctests in the sources finished, look at the " \
198− "results in $(BUILDDIR)/doctest/output.txt."
199−
200−.PHONY: coverage
201−coverage:
202− $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
203− @echo "Testing of coverage in the sources finished, look at the " \
204− "results in $(BUILDDIR)/coverage/python.txt."
13+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
20514
206−.PHONY: xml
207−xml:
208− $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
209− @echo
210− @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
15+.PHONY: help Makefile
21116
212−.PHONY: pseudoxml
213−pseudoxml:
214− $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
215− @echo
216− @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
17+# Catch-all target: route all unknown targets to Sphinx using the new
18+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19+%: Makefile
20+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
doc/conf.py+54−175
@@ -1,7 +1,7 @@
11# -*- coding: utf-8 -*-
22#
3−# servant documentation build configuration file, created by
4−# sphinx-quickstart on Mon Nov 23 13:24:36 2015.
3+# Servant documentation build configuration file, created by
4+# sphinx-quickstart on Fri Jul 6 11:38:51 2018.
55#
66# This file is execfile()d with the current directory set to its
77# containing dir.
@@ -12,20 +12,21 @@
1212# All configuration values have a default; values that are commented out
1313# serve to show the default.
1414
15−import sys
16−import os
17−import shlex
18−from recommonmark.parser import CommonMarkParser
19−
2015# If extensions (or modules to document with autodoc) are in another directory,
2116# add these directories to sys.path here. If the directory is relative to the
2217# documentation root, use os.path.abspath to make it absolute, like shown here.
23−#sys.path.insert(0, os.path.abspath('.'))
18+#
19+# import os
20+# import sys
21+# sys.path.insert(0, os.path.abspath('.'))
22+from recommonmark.parser import CommonMarkParser
23+
2424
2525# -- General configuration ------------------------------------------------
2626
2727# If your documentation needs a minimal Sphinx version, state it here.
28−#needs_sphinx = '1.0'
28+#
29+# needs_sphinx = '1.0'
2930
3031# Add any Sphinx extension module names here, as strings. They can be
3132# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -37,17 +38,15 @@ templates_path = ['_templates']
3738
3839# The suffix(es) of source filenames.
3940# You can specify multiple suffix as a list of string:
40−source_suffix = ['.md', '.rst', '.lhs']
41−
42−# The encoding of source files.
43−#source_encoding = 'utf-8-sig'
41+#
42+source_suffix = ['.rst', '.md', '.lhs']
4443
4544# The master toctree document.
4645master_doc = 'index'
4746
4847# General information about the project.
49−project = u'servant'
50−copyright = u'2016, Servant Contributors'
48+project = u'Servant'
49+copyright = u'2018, Servant Contributors'
5150author = u'Servant Contributors'
5251
5352# The version info for the project you're documenting, acts as replacement for
@@ -55,9 +54,9 @@ author = u'Servant Contributors'
5554# built documents.
5655#
5756# The short X.Y version.
58−# version = 'latest'
57+# version = u'0.14'
5958# The full version, including alpha/beta/rc tags.
60−# release = 'latest'
59+# release = u'0.14'
6160
6261# The language for content autogenerated by Sphinx. Refer to documentation
6362# for a list of supported languages.
@@ -66,45 +65,14 @@ author = u'Servant Contributors'
6665# Usually you set "language" from the command line for these cases.
6766language = None
6867
69−# There are two options for replacing |today|: either, you set today to some
70−# non-false value, then it is used:
71−#today = ''
72−# Else, today_fmt is used as the format for a strftime call.
73−#today_fmt = '%B %d, %Y'
74−
7568# List of patterns, relative to source directory, that match files and
7669# directories to ignore when looking for source files.
77−exclude_patterns = ['_build', 'venv']
78−
79−# The reST default role (used for this markup: `text`) to use for all
80−# documents.
81−#default_role = None
82−
83−# If true, '()' will be appended to :func: etc. cross-reference text.
84−#add_function_parentheses = True
85−
86−# If true, the current module name will be prepended to all description
87−# unit titles (such as .. function::).
88−#add_module_names = True
89−
90−# If true, sectionauthor and moduleauthor directives will be shown in the
91−# output. They are ignored by default.
92−#show_authors = False
70+# This patterns also effect to html_static_path and html_extra_path
71+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
9372
9473# The name of the Pygments (syntax highlighting) style to use.
9574pygments_style = 'sphinx'
9675
97−def setup(app):
98− from sphinx.highlighting import lexers
99− from pygments.lexers import HaskellLexer
100− lexers['haskell ignore'] = HaskellLexer(stripnl=False)
101−
102−# A list of ignored prefixes for module index sorting.
103−#modindex_common_prefix = []
104−
105−# If true, keep warnings as "system message" paragraphs in the built documents.
106−#keep_warnings = False
107−
10876# If true, `todo` and `todoList` produce output, else they produce nothing.
10977todo_include_todos = False
11078
@@ -113,157 +81,77 @@ todo_include_todos = False
11381
11482# The theme to use for HTML and HTML Help pages. See the documentation for
11583# a list of builtin themes.
84+#
11685html_theme = 'sphinx_rtd_theme'
11786
11887# Theme options are theme-specific and customize the look and feel of a theme
11988# further. For a list of options available for each theme, see the
12089# documentation.
121−#html_theme_options = {}
122−
123−# Add any paths that contain custom themes here, relative to this directory.
124−#html_theme_path = []
125−
126−# The name for this set of Sphinx documents. If None, it defaults to
127−# "<project> v<release> documentation".
128−#html_title = None
129−
130−# A shorter title for the navigation bar. Default is the same as html_title.
131−#html_short_title = None
132−
133−# The name of an image file (relative to this directory) to place at the top
134−# of the sidebar.
135−#html_logo = None
136−
137−# The name of an image file (within the static path) to use as favicon of the
138−# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
139−# pixels large.
140−#html_favicon = None
90+#
91+# html_theme_options = {}
14192
14293# Add any paths that contain custom static files (such as style sheets) here,
14394# relative to this directory. They are copied after the builtin static files,
14495# so a file named "default.css" will overwrite the builtin "default.css".
14596html_static_path = ['_static']
14697
147−# Add any extra paths that contain custom files (such as robots.txt or
148−# .htaccess) here, relative to this directory. These files are copied
149−# directly to the root of the documentation.
150−#html_extra_path = []
151−
152−# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
153−# using the given strftime format.
154−#html_last_updated_fmt = '%b %d, %Y'
155−
156−# If true, SmartyPants will be used to convert quotes and dashes to
157−# typographically correct entities.
158−#html_use_smartypants = True
159−
160−# Custom sidebar templates, maps document names to template names.
161−#html_sidebars = {}
162−
163−# Additional templates that should be rendered to pages, maps page names to
164−# template names.
165−#html_additional_pages = {}
166−
167−# If false, no module index is generated.
168−#html_domain_indices = True
169−
170−# If false, no index is generated.
171−#html_use_index = True
172−
173−# If true, the index is split into individual pages for each letter.
174−#html_split_index = False
175−
176−# If true, links to the reST sources are added to the pages.
177−#html_show_sourcelink = True
178−
179−# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
180−#html_show_sphinx = True
181−
182−# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
183−#html_show_copyright = True
184−
185−# If true, an OpenSearch description file will be output, and all pages will
186−# contain a <link> tag referring to it. The value of this option must be the
187−# base URL from which the finished HTML is served.
188−#html_use_opensearch = ''
189−
190−# This is the file name suffix for HTML files (e.g. ".xhtml").
191−#html_file_suffix = None
192−
193−# Language to be used for generating the HTML full-text search index.
194−# Sphinx supports the following languages:
195−# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
196−# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
197−#html_search_language = 'en'
98+# Custom sidebar templates, must be a dictionary that maps document names
99+# to template names.
100+#
101+# This is required for the alabaster theme
102+# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
103+html_sidebars = {
104+ '**': [
105+ 'relations.html', # needs 'show_related': True theme option to display
106+ 'searchbox.html',
107+ ]
108+}
198109
199−# A dictionary with options for the search language support, empty by default.
200−# Now only 'ja' uses this config value
201−#html_search_options = {'type': 'default'}
202110
203−# The name of a javascript file (relative to the configuration directory) that
204−# implements a search results scorer. If empty, the default will be used.
205−#html_search_scorer = 'scorer.js'
111+# -- Options for HTMLHelp output ------------------------------------------
206112
207113# Output file base name for HTML help builder.
208−htmlhelp_basename = 'servantdoc'
114+htmlhelp_basename = 'Servantdoc'
115+
209116
210117# -- Options for LaTeX output ---------------------------------------------
211118
212119latex_elements = {
213−# The paper size ('letterpaper' or 'a4paper').
214−#'papersize': 'letterpaper',
120+ # The paper size ('letterpaper' or 'a4paper').
121+ #
122+ # 'papersize': 'letterpaper',
215123
216−# The font size ('10pt', '11pt' or '12pt').
217−#'pointsize': '10pt',
124+ # The font size ('10pt', '11pt' or '12pt').
125+ #
126+ # 'pointsize': '10pt',
218127
219−# Additional stuff for the LaTeX preamble.
220−#'preamble': '',
128+ # Additional stuff for the LaTeX preamble.
129+ #
130+ # 'preamble': '',
221131
222−# Latex figure (float) alignment
223−#'figure_align': 'htbp',
132+ # Latex figure (float) alignment
133+ #
134+ # 'figure_align': 'htbp',
224135}
225136
226137# Grouping the document tree into LaTeX files. List of tuples
227138# (source start file, target name, title,
228139# author, documentclass [howto, manual, or own class]).
229140latex_documents = [
230− (master_doc, 'servant.tex', u'servant Documentation',
141+ (master_doc, 'Servant.tex', u'Servant Documentation',
231142 u'Servant Contributors', 'manual'),
232143]
233144
234−# The name of an image file (relative to this directory) to place at the top of
235−# the title page.
236−#latex_logo = None
237−
238−# For "manual" documents, if this is true, then toplevel headings are parts,
239−# not chapters.
240−#latex_use_parts = False
241−
242−# If true, show page references after internal links.
243−#latex_show_pagerefs = False
244−
245−# If true, show URL addresses after external links.
246−#latex_show_urls = False
247−
248−# Documents to append as an appendix to all manuals.
249−#latex_appendices = []
250−
251−# If false, no module index is generated.
252−#latex_domain_indices = True
253−
254145
255146# -- Options for manual page output ---------------------------------------
256147
257148# One entry per manual page. List of tuples
258149# (source start file, name, description, authors, manual section).
259150man_pages = [
260− (master_doc, 'servant', u'servant Documentation',
151+ (master_doc, 'servant', u'Servant Documentation',
261152 [author], 1)
262153]
263154
264−# If true, show URL addresses after external links.
265−#man_show_urls = False
266−
267155
268156# -- Options for Texinfo output -------------------------------------------
269157
@@ -271,24 +159,15 @@ man_pages = [
271159# (source start file, target name, title, author,
272160# dir menu entry, description, category)
273161texinfo_documents = [
274− (master_doc, 'servant', u'servant Documentation',
275− author, 'servant', 'One line description of project.',
276− 'Miscellaneous'),
162+ (master_doc, 'Servant', u'Servant Documentation',
163+ author, 'Servant', 'One line description of project.',
164+ 'Miscellaneous'),
277165]
278166
279−# Documents to append as an appendix to all manuals.
280−#texinfo_appendices = []
281−
282−# If false, no module index is generated.
283−#texinfo_domain_indices = True
284−
285−# How to display URL addresses: 'footnote', 'no', or 'inline'.
286−#texinfo_show_urls = 'footnote'
287−
288−# If true, do not generate a @detailmenu in the "Top" node's menu.
289−#texinfo_no_detailmenu = False
167+# -- Markdown -------------------------------------------------------------
290168
291169source_parsers = {
292170 '.md': CommonMarkParser,
293171 '.lhs': CommonMarkParser,
294172}
173+
doc/cookbook/basic-auth/basic-auth.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-basic-auth
1420 main-is: BasicAuth.lhs
doc/cookbook/db-postgres-pool/db-postgres-pool.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-db-postgres-pool
1420 main-is: PostgresPool.lhs
doc/cookbook/db-sqlite-simple/db-sqlite-simple.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-db-sqlite-simple
1420 main-is: DBConnection.lhs
doc/cookbook/file-upload/file-upload.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-file-upload
1420 main-is: FileUpload.lhs
doc/cookbook/generic/Generic.lhsnew file+106
@@ -0,0 +1,106 @@
1+# Using generics
2+
3+```haskell
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE DeriveGeneric #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE TypeOperators #-}
8+module Main (main, api, getLink, routesLinks, cliGet) where
9+
10+import Control.Exception (throwIO)
11+import Data.Proxy (Proxy (..))
12+import Network.Wai.Handler.Warp (run)
13+import System.Environment (getArgs)
14+
15+import Servant
16+import Servant.Client
17+
18+import Servant.API.Generic
19+import Servant.Client.Generic
20+import Servant.Server.Generic
21+```
22+
23+The usage is simple, if you only need a collection of routes.
24+First you define a record with field types prefixed by a parameter `route`:
25+
26+```haskell
27+data Routes route = Routes
28+ { _get :: route :- Capture "id" Int :> Get '[JSON] String
29+ , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
30+ }
31+ deriving (Generic)
32+```
33+
34+Then we'll use this data type to define API, links, server and client.
35+
36+## API
37+
38+You can get a `Proxy` of the API using `genericApi`:
39+
40+```haskell
41+api :: Proxy (ToServantApi Routes)
42+api = genericApi (Proxy :: Proxy Routes)
43+```
44+
45+It's recommented to use `genericApi` function, as then you'll get
46+better error message, for example if you forget to `derive Generic`.
47+
48+## Links
49+
50+The clear advantage of record-based generics approach, is that
51+we can get safe links very conviently. We don't need to define endpoint types,
52+as field accessors work as proxies:
53+
54+```haskell
55+getLink :: Int -> Link
56+getLink = fieldLink _get
57+```
58+
59+We can also get all links at once, as a record:
60+
61+```haskell
62+routesLinks :: Routes (AsLink Link)
63+routesLinks = allFieldLinks
64+```
65+
66+## Client
67+
68+Even more power starts to show when we generate a record of client functions.
69+Here we use `genericClientHoist` function, which let us simultaneously
70+hoist the monad, in this case from `ClientM` to `IO`.
71+
72+```haskell
73+cliRoutes :: Routes (AsClientT IO)
74+cliRoutes = genericClientHoist
75+ (\x -> runClientM x env >>= either throwIO return)
76+ where
77+ env = error "undefined environment"
78+
79+cliGet :: Int -> IO String
80+cliGet = _get cliRoutes
81+```
82+
83+## Server
84+
85+Finally, probably the most handy usage: we can convert record of handlers into
86+the server implementation:
87+
88+```haskell
89+record :: Routes AsServer
90+record = Routes
91+ { _get = return . show
92+ , _put = return . odd
93+ }
94+
95+app :: Application
96+app = genericServe record
97+
98+main :: IO ()
99+main = do
100+ args <- getArgs
101+ case args of
102+ ("run":_) -> do
103+ putStrLn "Starting cookbook-generic at http://localhost:8000"
104+ run 8000 app
105+ _ -> putStrLn "To run, pass 'run' argument: cabal new-run cookbook-generic run"
106+```
doc/cookbook/generic/generic.cabalnew file+31
@@ -0,0 +1,31 @@
1+name: cookbook-generic
2+version: 0.1
3+synopsis: Using custom monad to pass a state between handlers
4+homepage: http://haskell-servant.readthedocs.org/
5+license: BSD3
6+license-file: ../../../servant/LICENSE
7+author: Servant Contributors
8+maintainer: haskell-servant-maintainers@googlegroups.com
9+build-type: Simple
10+cabal-version: >=1.10
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
18+
19+executable cookbook-using-custom-monad
20+ main-is: Generic.lhs
21+ build-depends: base == 4.*
22+ , servant
23+ , servant-client
24+ , servant-client-core
25+ , servant-server
26+ , base-compat
27+ , warp >= 3.2
28+ , transformers >= 0.3
29+ default-language: Haskell2010
30+ ghc-options: -Wall -pgmL markdown-unlit
31+ build-tool-depends: markdown-unlit:markdown-unlit >= 0.4
doc/cookbook/https/https.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-https
1420 main-is: Https.lhs
doc/cookbook/index.rst+1
@@ -18,6 +18,7 @@ you name it!
1818 :maxdepth: 1
1919
2020 structuring-apis/StructuringApis.lhs
21+ generic/Generic.lhs
2122 https/Https.lhs
2223 db-sqlite-simple/DBConnection.lhs
2324 db-postgres-pool/PostgresPool.lhs
doc/cookbook/jwt-and-basic-auth/jwt-and-basic-auth.cabal+7−1
@@ -11,7 +11,13 @@ maintainer: haskell-servant-maintainers@googlegroups.com
1111category: Servant
1212build-type: Simple
1313cabal-version: >=1.10
14−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
14+tested-with:
15+ GHC==7.8.4
16+ GHC==7.10.3
17+ GHC==8.0.2
18+ GHC==8.2.2
19+ GHC==8.4.4
20+ GHC==8.6.1
1521
1622executable cookbook-jwt-and-basic-auth
1723 if !impl(ghc >= 7.10)
doc/cookbook/pagination/pagination.cabal+8−2
@@ -11,7 +11,13 @@ cabal-version: >=1.10
1111extra-source-files:
1212 Pagination.lhs
1313 dummy/Pagination.lhs
14−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
14+tested-with:
15+ GHC==7.8.4
16+ GHC==7.10.3
17+ GHC==8.0.2
18+ GHC==8.2.2
19+ GHC==8.4.4
20+ GHC==8.6.1
1521
1622executable cookbook-pagination
1723 main-is: Pagination.lhs
@@ -21,7 +27,7 @@ executable cookbook-pagination
2127
2228 if impl(ghc >= 8.0)
2329 hs-source-dirs: .
24− build-depends: base >= 4.8 && <4.12
30+ build-depends: base >= 4.8
2531 , aeson
2632 , servant
2733 , servant-server
doc/cookbook/structuring-apis/structuring-apis.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-structuring-apis
1420 main-is: StructuringApis.lhs
doc/cookbook/using-custom-monad/using-custom-monad.cabal+7−1
@@ -8,7 +8,13 @@ author: Servant Contributors
88maintainer: haskell-servant-maintainers@googlegroups.com
99build-type: Simple
1010cabal-version: >=1.10
11−tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
11+tested-with:
12+ GHC==7.8.4
13+ GHC==7.10.3
14+ GHC==8.0.2
15+ GHC==8.2.2
16+ GHC==8.4.4
17+ GHC==8.6.1
1218
1319executable cookbook-using-custom-monad
1420 main-is: UsingCustomMonad.lhs
doc/index.rst+11−9
@@ -3,22 +3,24 @@ servant – A Type-Level Web DSL
33
44.. image:: https://raw.githubusercontent.com/haskell-servant/servant/master/servant.png
55
6−**servant** is a set of packages for declaring web APIs at the type-level and
7−then using those API specifications to:
6+**servant** is a set of Haskell libraries for writing *type-safe* web
7+applications but also *deriving* clients (in Haskell and other languages) or
8+generating documentation for them, and more.
89
9−- write servers (this part of **servant** can be considered a web framework),
10−- obtain client functions (in haskell),
11−- generate client functions for other programming languages,
12−- generate documentation for your web applications
13−- and more...
10+This is achieved by taking as input a description of the web API
11+as a Haskell type. Servant is then able to check that your server-side request
12+handlers indeed implement your web API faithfully, or to automatically derive
13+Haskell functions that can hit a web application that implements this API,
14+generate a Swagger description or code for client functions in some other
15+languages directly.
1416
15−All in a type-safe manner.
17+If you would like to learn more, click the tutorial link below.
1618
1719.. toctree::
1820 :maxdepth: 2
1921
20− introduction.rst
2122 tutorial/index.rst
2223 cookbook/index.rst
2324 examples.md
2425 links.rst
26+ principles.rst
doc/make.batnew file+36
@@ -0,0 +1,36 @@
1+@ECHO OFF
2+
3+pushd %~dp0
4+
5+REM Command file for Sphinx documentation
6+
7+if "%SPHINXBUILD%" == "" (
8+ set SPHINXBUILD=sphinx-build
9+)
10+set SOURCEDIR=.
11+set BUILDDIR=_build
12+set SPHINXPROJ=Servant
13+
14+if "%1" == "" goto help
15+
16+%SPHINXBUILD% >NUL 2>NUL
17+if errorlevel 9009 (
18+ echo.
19+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
20+ echo.installed, then set the SPHINXBUILD environment variable to point
21+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
22+ echo.may add the Sphinx directory to PATH.
23+ echo.
24+ echo.If you don't have Sphinx installed, grab it from
25+ echo.http://sphinx-doc.org/
26+ exit /b 1
27+)
28+
29+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30+goto end
31+
32+:help
33+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34+
35+:end
36+popd
doc/introduction.rst → doc/principles.rstrenamed+2−2
@@ -1,5 +1,5 @@
1−Introduction
2−------------
1+Principles
2+----------
33
44**servant** has the following guiding principles:
55
doc/req.txtnew file+25
@@ -0,0 +1,25 @@
1+alabaster==0.7.11
2+argh==0.26.2
3+Babel==2.6.0
4+backports-abc==0.5
5+backports.ssl-match-hostname==3.5.0.1
6+certifi==2018.4.16
7+CommonMark==0.7.5
8+docutils==0.14
9+Jinja2==2.10
10+livereload==2.5.2
11+MarkupSafe==1.0
12+pathtools==0.1.2
13+Pygments==2.2.0
14+pytz==2018.5
15+PyYAML==3.13
16+recommonmark==0.4.0
17+singledispatch==3.4.0.3
18+six==1.11.0
19+snowballstemmer==1.2.1
20+Sphinx==1.6.5
21+sphinx-autobuild==0.7.1
22+sphinx_rtd_theme>=0.4.0
23+tornado==5.0.2
24+watchdog==0.8.3
25+wheel==0.31.1
doc/requirements.txt+2−24
@@ -1,25 +1,3 @@
1−alabaster==0.7.7
2−argh==0.26.1
3−Babel==2.2.0
4−backports-abc==0.4
5−backports.ssl-match-hostname==3.5.0.1
6−certifi==2015.11.20.1
7−CommonMark==0.5.4
8−docutils==0.12
9−Jinja2==2.8
10−livereload==2.4.1
11−MarkupSafe==0.23
12−pathtools==0.1.2
13−Pygments==2.2.0
14−pytz==2015.7
15−PyYAML==3.11
161recommonmark==0.4.0
17−singledispatch==3.4.0.3
18−six==1.10.0
19−snowballstemmer==1.2.1
20−Sphinx==1.3.6
21−sphinx-autobuild==0.5.2
22−sphinx-rtd-theme==0.1.9
23−tornado==4.3
24−watchdog==0.8.3
25−wheel==0.26.0
2+Sphinx==1.7.5
3+sphinx_rtd_theme>=0.4.0
doc/setup.pynew file+11
@@ -0,0 +1,11 @@
1+from setuptools import setup
2+
3+setup(name='servant-rtd',
4+ version='0.0.1',
5+ description='Documentation for the haskell-servant platform',
6+ url='https://github.com/haskell-servant/servant',
7+ author='Servant Contributors',
8+ author_email='haskell-servant-maintainers@googlegroups.com',
9+ license='BSD3',
10+ zip_safe=False)
11+
doc/tutorial/Javascript.lhs+1−2
@@ -477,7 +477,7 @@ data AngularOptions = AngularOptions
477477 }
478478```
479479
480−# Custom function name builder
480+## Custom function name builder
481481
482482Servant comes with three name builders included:
483483
@@ -518,4 +518,3 @@ var get_books = function(q, onSuccess, onError)
518518}
519519
520520```
521−
doc/tutorial/index.rst+21−2
@@ -3,9 +3,28 @@ Tutorial
33
44This is an introductory tutorial to **servant**. Whilst browsing is fine, it makes more sense if you read the sections in order, or at least read the first section before anything else.
55
6−(Any comments, issues or feedback about the tutorial can be submitted
7−to `servant's issue tracker <http://github.com/haskell-servant/servant/issues>`_.)
6+Any comments, issues or feedback about the tutorial can be submitted
7+to `servant's issue tracker <http://github.com/haskell-servant/servant/issues>`_.
88
9+In fact, the whole tutorial is a `cabal <https://cabal.readthedocs.io/en/latest/>`_
10+project and can be built and played with locally as follows:
11+
12+.. code-block:: bash
13+
14+ $ git clone https://github.com/haskell-servant/servant.git
15+ $ cd servant
16+ # build
17+ $ cabal new-build tutorial
18+ # load in ghci to play with it
19+ $ cabal new-repl tutorial
20+
21+The code can be found in the `*.lhs` files under `doc/tutorial/` in the
22+repository. Feel free to edit it while you're reading this documentation and
23+see the effect of your changes.
24+
25+`Nix <https://nixos.org/nix/>`_ users should feel free to take a look at
26+the `nix/shell.nix` file in the repository and use it to provision a suitable
27+environment to build and run the examples.
928
1029.. toctree::
1130 :maxdepth: 1
doc/tutorial/tutorial.cabal+5−4
@@ -17,7 +17,8 @@ tested-with:
1717 GHC==7.10.3
1818 GHC==8.0.2
1919 GHC==8.2.2
20− GHC==8.4.3
20+ GHC==8.4.4
21+ GHC==8.6.1
2122extra-source-files:
2223 static/index.html
2324 static/ui.js
@@ -35,7 +36,7 @@ library
3536 -- Packages `servant` depends on.
3637 -- We don't need to specify bounds here as this package is never released.
3738 build-depends:
38− base >= 4.7 && <4.12
39+ base >= 4.7 && <4.13
3940 , aeson
4041 , aeson-compat
4142 , attoparsec
@@ -75,8 +76,8 @@ library
7576 , time >= 1.4.2 && < 1.9
7677
7778 -- For legacy tools, we need to specify build-depends too
78− build-depends: markdown-unlit >= 0.4.1 && <0.5
79− build-tool-depends: markdown-unlit:markdown-unlit >= 0.4.1 && <0.5
79+ build-depends: markdown-unlit >= 0.5.0 && <0.6
80+ build-tool-depends: markdown-unlit:markdown-unlit >= 0.5.0 && <0.6
8081
8182test-suite spec
8283 type: exitcode-stdio-1.0
servant-client-core/CHANGELOG.md+50−2
@@ -1,13 +1,61 @@
11[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md)
22[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
33
4+0.14.1
5+------
6+
7+- Merge in `servant-generic` (by [Patrick Chilton](https://github.com/chpatrick))
8+ into `servant` (`Servant.API.Generic`),
9+ `servant-client-code` (`Servant.Client.Generic`)
10+ and `servant-server` (`Servant.Server.Generic`).
11+
4120.14
513----
614
7−- Add a `hoistClientMonad` method to the `HasClient` typeclass, for
8− changing the monad in which client functions run.
15+- `Stream` takes a status code argument
16+
17+ ```diff
18+ -Stream method framing ctype a
19+ +Stream method status framing ctype a
20+ ```
21+
22+ ([#966](https://github.com/haskell-servant/servant/pull/966)
23+ [#972](https://github.com/haskell-servant/servant/pull/972))
24+
25+- `ToStreamGenerator` definition changed, so it's possible to write an instance
26+ for conduits.
27+
28+ ```diff
29+ -class ToStreamGenerator f a where
30+ - toStreamGenerator :: f a -> StreamGenerator a
31+ +class ToStreamGenerator a b | a -> b where
32+ + toStreamGenerator :: a -> StreamGenerator b
33+ ```
34+
35+ ([#959](https://github.com/haskell-servant/servant/pull/959))
36+
37+- Added `NoFraming` streaming strategy
38+ ([#959](https://github.com/haskell-servant/servant/pull/959))
39+
40+- *servant-client-core* Free `Client` implementation.
41+ Useful for testing `HasClient` instances.
42+ ([#920](https://github.com/haskell-servant/servant/pull/920))
43+
44+- *servant-client-core* Add `hoistClient` to `HasClient`.
45+ Just like `hoistServer` allows us to change the monad in which request handlers
46+ of a web application live in, we also have `hoistClient` for changing the monad
47+ in which *client functions* live.
48+ Read [tutorial section for more information](https://haskell-servant.readthedocs.io/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
949 ([#936](https://github.com/haskell-servant/servant/pull/936))
1050
51+ iF you have own combinators, you'll need to define a new method of
52+ `HasClient` class, for example:
53+
54+ ```haskell
55+ type Client m (MyCombinator :> api) = MyValue :> Client m api
56+ hoistClientMonad pm _ nt cl = hoistClientMonad pm (Proxy :: Proxy api) nt . cl
57+ ```
58+
11590.13.0.1
1260--------
1361
servant-client-core/servant-client-core.cabal+23−19
@@ -1,9 +1,13 @@
11name: servant-client-core
2−version: 0.14
2+version: 0.14.1
3+x-revision: 2
4+
35synopsis: Core functionality and class for client function generation for servant APIs
6+category: Web, Servant
47description:
58 This library provides backend-agnostic generation of client functions. For
69 more information, see the README.
10+
711license: BSD3
812license-file: LICENSE
913author: Servant Contributors
@@ -12,7 +16,6 @@ homepage: http://haskell-servant.readthedocs.org/
1216bug-reports: http://github.com/haskell-servant/servant/issues
1317cabal-version: >=1.10
1418copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2017 Servant Contributors
15−category: Web
1619build-type: Simple
1720extra-source-files:
1821 include/*.h
@@ -23,7 +26,8 @@ tested-with:
2326 GHC==7.10.3
2427 GHC==8.0.2
2528 GHC==8.2.2
26− GHC==8.4.3
29+ GHC==8.4.4
30+ GHC==8.6.1
2731
2832source-repository head
2933 type: git
@@ -33,6 +37,7 @@ library
3337 exposed-modules:
3438 Servant.Client.Core
3539 Servant.Client.Free
40+ Servant.Client.Generic
3641 Servant.Client.Core.Reexport
3742 Servant.Client.Core.Internal.Auth
3843 Servant.Client.Core.Internal.BaseUrl
@@ -48,33 +53,32 @@ library
4853 --
4954 -- note: mtl lower bound is so low because of GHC-7.8
5055 build-depends:
51− base >= 4.7 && < 4.12
56+ base >= 4.7 && < 4.13
5257 , bytestring >= 0.10.4.0 && < 0.11
53− , containers >= 0.5.5.1 && < 0.6
54− , mtl >= 2.1 && < 2.3
58+ , containers >= 0.5.5.1 && < 0.7
5559 , text >= 1.2.3.0 && < 1.3
5660
5761 if !impl(ghc >= 8.0)
5862 build-depends:
59− semigroups >=0.18.3 && <0.19
63+ semigroups >=0.18.4 && <0.19
6064
6165 -- Servant dependencies
6266 build-depends:
63− servant == 0.14.*
67+ servant >= 0.14.1 && <0.15
6468
6569 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
6670 -- Here can be exceptions if we really need features from the newer versions.
6771 build-depends:
68− base-compat >= 0.9.3 && < 0.11
72+ base-compat >= 0.10.1 && < 0.11
6973 , base64-bytestring >= 1.0.0.1 && < 1.1
70− , exceptions >= 0.8.3 && < 0.11
71− , free >= 5.0.1 && < 5.1
72− , generics-sop >= 0.3.1.0 && < 0.4
73− , http-api-data >= 0.3.7.1 && < 0.4
74− , http-media >= 0.7.1.1 && < 0.8
75− , http-types >= 0.12 && < 0.13
74+ , exceptions >= 0.10.0 && < 0.11
75+ , free >= 5.0.2 && < 5.2
76+ , generics-sop >= 0.3.2.0 && < 0.5
77+ , http-api-data >= 0.3.8.1 && < 0.4
78+ , http-media >= 0.7.1.2 && < 0.8
79+ , http-types >= 0.12.1 && < 0.13
7680 , network-uri >= 2.6.1.0 && < 2.7
77− , safe >= 0.3.15 && < 0.4
81+ , safe >= 0.3.17 && < 0.4
7882
7983 hs-source-dirs: src
8084 default-language: Haskell2010
@@ -99,8 +103,8 @@ test-suite spec
99103 -- Additonal dependencies
100104 build-depends:
101105 deepseq >= 1.3.0.2 && <1.5
102− , hspec >= 2.4.4 && <2.6
103− , QuickCheck >= 2.10.1 && < 2.12
106+ , hspec >= 2.4.1 && <2.6
107+ , QuickCheck >= 2.11.3 && < 2.13
104108
105109 build-tool-depends:
106− hspec-discover:hspec-discover >= 2.4.4 && <2.6
110+ hspec-discover:hspec-discover >= 2.5.1 && <2.6
servant-client-core/src/Servant/Client/Generic.hsnew file+51
@@ -0,0 +1,51 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+module Servant.Client.Generic (
8+ AsClientT,
9+ genericClient,
10+ genericClientHoist,
11+ ) where
12+
13+import Data.Proxy
14+ (Proxy (..))
15+
16+import Servant.API.Generic
17+import Servant.Client.Core
18+
19+-- | A type that specifies that an API reocrd contains a client implementation.
20+data AsClientT (m :: * -> *)
21+instance GenericMode (AsClientT m) where
22+ type AsClientT m :- api = Client m api
23+
24+-- | Generate a record of client functions.
25+genericClient
26+ :: forall routes m.
27+ ( HasClient m (ToServantApi routes)
28+ , GenericServant routes (AsClientT m)
29+ , Client m (ToServantApi routes) ~ ToServant routes (AsClientT m)
30+ )
31+ => routes (AsClientT m)
32+genericClient
33+ = fromServant
34+ $ clientIn (Proxy :: Proxy (ToServantApi routes)) (Proxy :: Proxy m)
35+
36+-- | 'genericClient' but with 'hoistClientMonad' in between.
37+genericClientHoist
38+ :: forall routes m n.
39+ ( HasClient m (ToServantApi routes)
40+ , GenericServant routes (AsClientT n)
41+ , Client n (ToServantApi routes) ~ ToServant routes (AsClientT n)
42+ )
43+ => (forall x. m x -> n x) -- ^ natural transformation
44+ -> routes (AsClientT n)
45+genericClientHoist nt
46+ = fromServant
47+ $ hoistClientMonad m api nt
48+ $ clientIn api m
49+ where
50+ m = Proxy :: Proxy m
51+ api = Proxy :: Proxy (ToServantApi routes)
servant-client-ghcjs/servant-client-ghcjs.cabal+3−3
@@ -31,10 +31,10 @@ library
3131 Servant.Client.Ghcjs
3232 Servant.Client.Internal.XhrClient
3333 build-depends:
34− base >= 4.7 && < 4.12
34+ base >= 4.7 && < 4.13
3535 , bytestring >= 0.10 && < 0.11
3636 , case-insensitive >= 1.2.0.0 && < 1.3.0.0
37− , containers >= 0.5 && < 0.6
37+ , containers >= 0.5 && < 0.7
3838 , exceptions >= 0.8 && < 0.11
3939 , ghcjs-base >= 0.2.0.0 && < 0.3.0.0
4040 , ghcjs-prim >= 0.1.0.0 && < 0.2.0.0
@@ -42,7 +42,7 @@ library
4242 , http-types >= 0.12 && < 0.13
4343 , monad-control >= 1.0.0.4 && < 1.1
4444 , mtl >= 2.1 && < 2.3
45− , semigroupoids >= 4.3 && < 5.3
45+ , semigroupoids >= 4.3 && < 5.4
4646 , servant-client-core == 0.14.*
4747 , string-conversions >= 0.3 && < 0.5
4848 , transformers >= 0.3 && < 0.6
servant-client/CHANGELOG.md+37−2
@@ -4,10 +4,45 @@
440.14
55----
66
7−- Add `hoistClient` for changing the monad in which
8− client functions run.
7+- `Stream` takes a status code argument
8+
9+ ```diff
10+ -Stream method framing ctype a
11+ +Stream method status framing ctype a
12+ ```
13+
14+ ([#966](https://github.com/haskell-servant/servant/pull/966)
15+ [#972](https://github.com/haskell-servant/servant/pull/972))
16+
17+- `ToStreamGenerator` definition changed, so it's possible to write an instance
18+ for conduits.
19+
20+ ```diff
21+ -class ToStreamGenerator f a where
22+ - toStreamGenerator :: f a -> StreamGenerator a
23+ +class ToStreamGenerator a b | a -> b where
24+ + toStreamGenerator :: a -> StreamGenerator b
25+ ```
26+
27+ ([#959](https://github.com/haskell-servant/servant/pull/959))
28+
29+- Added `NoFraming` streaming strategy
30+ ([#959](https://github.com/haskell-servant/servant/pull/959))
31+
32+- *servant-client-core* Add `hoistClient` to `HasClient`.
33+ Just like `hoistServer` allows us to change the monad in which request handlers
34+ of a web application live in, we also have `hoistClient` for changing the monad
35+ in which *client functions* live.
36+ Read [tutorial section for more information](https://haskell-servant.readthedocs.io/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
937 ([#936](https://github.com/haskell-servant/servant/pull/936))
1038
39+- *servant-client* Add more constructors to `RequestBody`, including
40+ `RequestBodyStream`.
41+ *Note:* we are looking for http-library agnostic API,
42+ so the might change again soon.
43+ Tell us which constructors are useful for you!
44+ ([#913](https://github.com/haskell-servant/servant/pull/913))
45+
11460.13.0.1
1247--------
1348
servant-client/servant-client.cabal+28−29
@@ -1,6 +1,9 @@
11name: servant-client
22version: 0.14
3+x-revision: 3
4+
35synopsis: automatical derivation of querying functions for servant webservices
6+category: Servant, Web
47description:
58 This library lets you derive automatically Haskell functions that
69 let you query each endpoint of a <http://hackage.haskell.org/package/servant servant> webservice.
@@ -8,12 +11,12 @@ description:
811 See <http://haskell-servant.readthedocs.org/en/stable/tutorial/Client.html the client section of the tutorial>.
912 .
1013 <https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md CHANGELOG>
14+
1115license: BSD3
1216license-file: LICENSE
1317author: Servant Contributors
1418maintainer: haskell-servant-maintainers@googlegroups.com
1519copyright: 2014-2016 Zalora South East Asia Pte Ltd, 2016-2017 Servant Contributors
16−category: Servant, Web
1720build-type: Simple
1821cabal-version: >=1.10
1922tested-with:
@@ -21,7 +24,9 @@ tested-with:
2124 GHC==7.10.3
2225 GHC==8.0.2
2326 GHC==8.2.2
24− GHC==8.4.3
27+ GHC==8.4.4
28+ GHC==8.6.1
29+
2530homepage: http://haskell-servant.readthedocs.org/
2631Bug-reports: http://github.com/haskell-servant/servant/issues
2732extra-source-files:
@@ -42,16 +47,16 @@ library
4247 --
4348 -- note: mtl lower bound is so low because of GHC-7.8
4449 build-depends:
45− base >= 4.7 && < 4.12
50+ base >= 4.7 && < 4.13
4651 , bytestring >= 0.10.4.0 && < 0.11
47− , containers >= 0.5.5.1 && < 0.6
52+ , containers >= 0.5.5.1 && < 0.7
4853 , mtl >= 2.1 && < 2.3
4954 , text >= 1.2.3.0 && < 1.3
5055 , time >= 1.4.2 && < 1.9
5156 , transformers >= 0.3.0.0 && < 0.6
5257
5358 if !impl(ghc >= 8.0)
54− build-depends: semigroups >=0.18.3 && <0.19
59+ build-depends: semigroups >=0.18.4 && <0.19
5560
5661 -- Servant dependencies
5762 build-depends:
@@ -60,19 +65,16 @@ library
6065 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
6166 -- Here can be exceptions if we really need features from the newer versions.
6267 build-depends:
63− aeson >= 1.2.3.0 && < 1.5
64− , base-compat >= 0.9.3 && < 0.11
65− , attoparsec >= 0.13.2.0 && < 0.14
66− , http-client >= 0.5.7.1 && < 0.6
67− , http-client-tls >= 0.3.5.1 && < 0.4
68− , http-media >= 0.7.1.1 && < 0.8
69− , http-types >= 0.12 && < 0.13
70− , exceptions >= 0.8.3 && < 0.11
71− , monad-control >= 1.0.0.4 && < 1.1
72− , semigroupoids >= 5.2.1 && < 5.3
73− , stm >= 2.4.4.1 && < 2.5
74− , transformers-base >= 0.4.4 && < 0.5
75− , transformers-compat >= 0.5.1 && < 0.7
68+ base-compat >= 0.10.1 && < 0.11
69+ , http-client >= 0.5.12 && < 0.6
70+ , http-media >= 0.7.1.2 && < 0.8
71+ , http-types >= 0.12.1 && < 0.13
72+ , exceptions >= 0.10.0 && < 0.11
73+ , monad-control >= 1.0.2.3 && < 1.1
74+ , semigroupoids >= 5.2.2 && < 5.4
75+ , stm >= 2.4.5.0 && < 2.6
76+ , transformers-base >= 0.4.5.2 && < 0.5
77+ , transformers-compat >= 0.6.2 && < 0.7
7678
7779 hs-source-dirs: src
7880 default-language: Haskell2010
@@ -97,10 +99,8 @@ test-suite spec
9799 , aeson
98100 , base-compat
99101 , bytestring
100− , containers
101102 , http-api-data
102103 , http-client
103− , http-media
104104 , http-types
105105 , mtl
106106 , servant-client
@@ -117,18 +117,16 @@ test-suite spec
117117
118118 -- Additonal dependencies
119119 build-depends:
120− deepseq >= 1.3.0.2 && < 1.5
121− , generics-sop >= 0.3.1.0 && < 0.4
122− , hspec >= 2.4.4 && < 2.6
120+ generics-sop >= 0.3.2.0 && < 0.5
121+ , hspec >= 2.5.1 && < 2.6
123122 , HUnit >= 1.6 && < 1.7
124− , random-bytestring >= 0.1 && < 0.2
125− , network >= 2.6.3.2 && < 2.8
126− , QuickCheck >= 2.10.1 && < 2.12
127− , servant == 0.13.*
128− , servant-server == 0.13.*
123+ , network >= 2.6.3.2 && < 2.9
124+ , QuickCheck >= 2.10.1 && < 2.13
125+ , servant == 0.14.*
126+ , servant-server == 0.14.*
129127
130128 build-tool-depends:
131− hspec-discover:hspec-discover >= 2.4.4 && < 2.6
129+ hspec-discover:hspec-discover >= 2.5.1 && < 2.6
132130
133131test-suite readme
134132 type: exitcode-stdio-1.0
@@ -136,3 +134,4 @@ test-suite readme
136134 build-depends: base, servant, http-client, text, servant-client, markdown-unlit
137135 build-tool-depends: markdown-unlit:markdown-unlit
138136 ghc-options: -pgmL markdown-unlit
137+ default-language: Haskell2010
servant-client/test/Servant/StreamSpec.hs+2
@@ -130,6 +130,7 @@ streamSpec = beforeAll (CS.startWaiApp server) $ afterAll CS.endWaiApp $ do
130130 jrb = Just (Right bob)
131131 runResultStream res `shouldReturn` (jra, jrb, jra, Nothing)
132132
133+{-
133134 it "streams in constant memory" $ \(_, baseUrl) -> do
134135 Right (ResultStream res) <- runClient getGetALot baseUrl
135136 let consumeNChunks n = replicateM_ n (res void)
@@ -140,6 +141,7 @@ streamSpec = beforeAll (CS.startWaiApp server) $ afterAll CS.endWaiApp $ do
140141 memUsed <- currentBytesUsed <$> getGCStats
141142#endif
142143 memUsed `shouldSatisfy` (< megabytes 22)
144+-}
143145
144146megabytes :: Num a => a -> a
145147megabytes n = n * (1000 ^ (2 :: Int))
servant-docs/servant-docs.cabal+10−6
@@ -1,19 +1,21 @@
11name: servant-docs
22version: 0.11.2
3−x-revision: 4
3+x-revision: 6
4+
45synopsis: generate API docs for your servant webservice
6+category: Servant, Web
57description:
68 Library for generating API docs from a servant API definition.
79 .
810 Runnable example <https://github.com/haskell-servant/servant/blob/master/servant-docs/example/greet.hs here>.
911 .
1012 <https://github.com/haskell-servant/servant/blob/master/servant-docs/CHANGELOG.md CHANGELOG>
13+
1114license: BSD3
1215license-file: LICENSE
1316author: Servant Contributors
1417maintainer: haskell-servant-maintainers@googlegroups.com
1518copyright: 2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors
16−category: Servant, Web
1719build-type: Simple
1820cabal-version: >=1.10
1921tested-with:
@@ -21,7 +23,9 @@ tested-with:
2123 GHC==7.10.3
2224 GHC==8.0.2
2325 GHC==8.2.2
24− GHC==8.4.3
26+ GHC==8.4.4
27+ GHC==8.6.1
28+
2529homepage: http://haskell-servant.readthedocs.org/
2630Bug-reports: http://github.com/haskell-servant/servant/issues
2731extra-source-files:
@@ -43,7 +47,7 @@ library
4347 --
4448 -- note: mtl lower bound is so low because of GHC-7.8
4549 build-depends:
46− base >= 4.7 && < 4.12
50+ base >= 4.7 && < 4.13
4751 , bytestring >= 0.10.4.0 && < 0.11
4852 , text >= 1.2.3.0 && < 1.3
4953
@@ -64,9 +68,9 @@ library
6468 , case-insensitive >= 1.2.0.10 && < 1.3
6569 , control-monad-omega >= 0.3.1 && < 0.4
6670 , hashable >= 1.2.6.1 && < 1.3
67− , http-media >= 0.7.1.1 && < 0.8
71+ , http-media >= 0.7.0 && < 0.8
6872 , http-types >= 0.12 && < 0.13
69− , lens >= 4.15.4 && < 4.17
73+ , lens >= 4.15.4 && < 4.18
7074 , string-conversions >= 0.4.0.1 && < 0.5
7175 , unordered-containers >= 0.2.8.0 && < 0.3
7276
servant-foreign/servant-foreign.cabal+8−4
@@ -1,6 +1,9 @@
11name: servant-foreign
22version: 0.11.1
3+x-revision: 4
4+
35synopsis: Helpers for generating clients for servant APIs in any programming language
6+category: Servant, Web
47description:
58 Helper types and functions for generating client functions for servant APIs in any programming language
69 .
@@ -9,12 +12,12 @@ description:
912 See the servant-js package for an example
1013 .
1114 <https://github.com/haskell-servant/servant/blob/master/servant-foreign/CHANGELOG.md CHANGELOG>
15+
1216license: BSD3
1317license-file: LICENSE
1418author: Servant Contributors
1519maintainer: haskell-servant-maintainers@googlegroups.com
1620copyright: 2015-2016 Servant Contributors
17−category: Servant, Web
1821build-type: Simple
1922cabal-version: >=1.10
2023extra-source-files:
@@ -27,7 +30,8 @@ tested-with:
2730 GHC==7.10.3
2831 GHC==8.0.2
2932 GHC==8.2.2
30− GHC==8.4.3
33+ GHC==8.4.4
34+ GHC==8.6.1
3135
3236source-repository head
3337 type: git
@@ -43,7 +47,7 @@ library
4347 --
4448 -- note: mtl lower bound is so low because of GHC-7.8
4549 build-depends:
46− base >= 4.7 && <4.12
50+ base >= 4.7 && <4.13
4751 , text >= 1.2.3.0 && < 1.3
4852
4953 if !impl(ghc >= 8.0)
@@ -58,7 +62,7 @@ library
5862 -- Here can be exceptions if we really need features from the newer versions.
5963 build-depends:
6064 base-compat >= 0.9.3 && <0.11
61− , lens >= 4.15.4 && <4.17
65+ , lens >= 4.15.4 && <4.18
6266 , http-types >= 0.12 && < 0.13
6367
6468 hs-source-dirs: src
servant-server/CHANGELOG.md+56
@@ -1,6 +1,62 @@
11[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md)
22[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
33
4+0.14.1
5+------
6+
7+- Merge in `servant-generic` (by [Patrick Chilton](https://github.com/chpatrick))
8+ into `servant` (`Servant.API.Generic`),
9+ `servant-client-code` (`Servant.Client.Generic`)
10+ and `servant-server` (`Servant.Server.Generic`).
11+
12+- *servant-server* Deprecate `Servant.Utils.StaticUtils`, use `Servant.Server.StaticUtils`.
13+
14+0.14
15+----
16+
17+- `Stream` takes a status code argument
18+
19+ ```diff
20+ -Stream method framing ctype a
21+ +Stream method status framing ctype a
22+ ```
23+
24+ ([#966](https://github.com/haskell-servant/servant/pull/966)
25+ [#972](https://github.com/haskell-servant/servant/pull/972))
26+
27+- `ToStreamGenerator` definition changed, so it's possible to write an instance
28+ for conduits.
29+
30+ ```diff
31+ -class ToStreamGenerator f a where
32+ - toStreamGenerator :: f a -> StreamGenerator a
33+ +class ToStreamGenerator a b | a -> b where
34+ + toStreamGenerator :: a -> StreamGenerator b
35+ ```
36+
37+ ([#959](https://github.com/haskell-servant/servant/pull/959))
38+
39+- Added `NoFraming` streaming strategy
40+ ([#959](https://github.com/haskell-servant/servant/pull/959))
41+
42+- *servant-server* File serving in polymorphic monad.
43+ i.e. Generalised types of `serveDirectoryFileServer` etc functions in
44+ `Servant.Utils.StaticFiles`
45+ ([#953](https://github.com/haskell-servant/servant/pull/953))
46+
47+- *servant-server* `ReqBody` content type check is recoverable.
48+ This allows writing APIs like:
49+
50+ ```haskell
51+ ReqBody '[JSON] Int :> Post '[PlainText] Int
52+ :<|> ReqBody '[PlainText] Int :> Post '[PlainText] Int
53+ ```
54+
55+ which is useful when handlers are subtly different,
56+ for example may do less work.
57+ ([#937](https://github.com/haskell-servant/servant/pull/937))
58+
59+
4600.13.0.1
561--------
662
servant-server/servant-server.cabal+40−38
@@ -1,6 +1,9 @@
11name: servant-server
2−version: 0.14
2+version: 0.14.1
3+x-revision: 1
4+
35synopsis: A family of combinators for defining webservices APIs and serving them
6+category: Servant, Web
47description:
58 A family of combinators for defining webservices APIs and serving them
69 .
@@ -11,6 +14,7 @@ description:
1114 a webserver that serves this API, using this package.
1215 .
1316 <https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md CHANGELOG>
17+
1418homepage: http://haskell-servant.readthedocs.org/
1519Bug-reports: http://github.com/haskell-servant/servant/issues
1620license: BSD3
@@ -18,7 +22,6 @@ license-file: LICENSE
1822author: Servant Contributors
1923maintainer: haskell-servant-maintainers@googlegroups.com
2024copyright: 2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors
21−category: Servant, Web
2225build-type: Custom
2326cabal-version: >=1.10
2427tested-with:
@@ -26,7 +29,9 @@ tested-with:
2629 GHC==7.10.3
2730 GHC==8.0.2
2831 GHC==8.2.2
29− GHC==8.4.3
32+ GHC==8.4.4
33+ GHC==8.6.1
34+
3035extra-source-files:
3136 include/*.h
3237 CHANGELOG.md
@@ -40,13 +45,14 @@ custom-setup
4045 setup-depends:
4146 base >= 4 && <5,
4247 Cabal,
43− cabal-doctest >= 1.0.1 && <1.1
48+ cabal-doctest >= 1.0.6 && <1.1
4449
4550library
4651 exposed-modules:
4752 Servant
4853 Servant.Server
4954 Servant.Server.Experimental.Auth
55+ Servant.Server.Generic
5056 Servant.Server.Internal
5157 Servant.Server.Internal.BasicAuth
5258 Servant.Server.Internal.Context
@@ -54,6 +60,10 @@ library
5460 Servant.Server.Internal.Router
5561 Servant.Server.Internal.RoutingApplication
5662 Servant.Server.Internal.ServantErr
63+ Servant.Server.StaticFiles
64+
65+ -- deprecated
66+ exposed-modules:
5767 Servant.Utils.StaticFiles
5868
5969 -- Bundled with GHC: Lower bound to not force re-installs
@@ -61,9 +71,9 @@ library
6171 --
6272 -- note: mtl lower bound is so low because of GHC-7.8
6373 build-depends:
64− base >= 4.7 && < 4.12
74+ base >= 4.7 && < 4.13
6575 , bytestring >= 0.10.4.0 && < 0.11
66− , containers >= 0.5.5.1 && < 0.6
76+ , containers >= 0.5.5.1 && < 0.7
6777 , mtl >= 2.1 && < 2.3
6878 , text >= 1.2.3.0 && < 1.3
6979 , transformers >= 0.3.0.0 && < 0.6
@@ -71,37 +81,31 @@ library
7181
7282 if !impl(ghc >= 8.0)
7383 build-depends:
74− semigroups >= 0.18.3 && < 0.19
84+ semigroups >= 0.18.4 && < 0.19
7585
7686 -- Servant dependencies
7787 build-depends:
78− servant == 0.14.*
88+ servant >= 0.14.1 && <0.15
7989
8090 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
8191 -- Here can be exceptions if we really need features from the newer versions.
8292 build-depends:
83− aeson >= 1.2.3.0 && < 1.5
84− , base-compat >= 0.9.3 && < 0.11
85− , attoparsec >= 0.13.2.0 && < 0.14
93+ base-compat >= 0.10.1 && < 0.11
8694 , base64-bytestring >= 1.0.0.1 && < 1.1
87− , exceptions >= 0.8.3 && < 0.11
88− , http-api-data >= 0.3.7.1 && < 0.4
89− , http-media >= 0.7.1.1 && < 0.8
90− , http-types >= 0.12 && < 0.13
95+ , exceptions >= 0.10.0 && < 0.11
96+ , http-api-data >= 0.3.8.1 && < 0.4
97+ , http-media >= 0.7.1.2 && < 0.8
98+ , http-types >= 0.12.1 && < 0.13
9199 , network-uri >= 2.6.1.0 && < 2.7
92− , monad-control >= 1.0.0.4 && < 1.1
93− , network >= 2.6.3.2 && < 2.8
94− , safe >= 0.3.15 && < 0.4
95− , split >= 0.2.3.2 && < 0.3
100+ , monad-control >= 1.0.2.3 && < 1.1
101+ , network >= 2.6.3.5 && < 2.9
96102 , string-conversions >= 0.4.0.1 && < 0.5
97− , system-filepath >= 0.4 && < 0.5
98− , resourcet >= 1.1.9 && < 1.3
103+ , resourcet >= 1.1.11 && < 1.3
99104 , tagged >= 0.8.5 && < 0.9
100105 , transformers-base >= 0.4.4 && < 0.5
101− , transformers-compat >= 0.5.1 && < 0.7
106+ , transformers-compat >= 0.6.2 && < 0.7
102107 , wai >= 3.2.1.1 && < 3.3
103108 , wai-app-static >= 3.1.6.1 && < 3.2
104− , warp >= 3.2.13 && < 3.3
105109 , word8 >= 0.1.3 && < 0.2
106110
107111 hs-source-dirs: src
@@ -120,11 +124,13 @@ executable greet
120124 base
121125 , servant
122126 , servant-server
123− , aeson
124− , warp
125127 , wai
126128 , text
127129
130+ build-depends:
131+ aeson >= 1.3.1.1 && < 1.5
132+ , warp >= 3.2.13 && < 3.3
133+
128134test-suite spec
129135 type: exitcode-stdio-1.0
130136 ghc-options: -Wall
@@ -137,24 +143,21 @@ test-suite spec
137143 Servant.Server.Internal.ContextSpec
138144 Servant.Server.Internal.RoutingApplicationSpec
139145 Servant.Server.RouterSpec
146+ Servant.Server.StaticFilesSpec
140147 Servant.Server.StreamingSpec
141148 Servant.Server.UsingContextSpec
142149 Servant.Server.UsingContextSpec.TestCombinators
143150 Servant.HoistSpec
144151 Servant.ServerSpec
145− Servant.Utils.StaticFilesSpec
146152
147153 -- Dependencies inherited from the library. No need to specify bounds.
148154 build-depends:
149155 base
150156 , base-compat
151− , aeson
152157 , base64-bytestring
153158 , bytestring
154− , exceptions
155159 , http-types
156160 , mtl
157− , network
158161 , resourcet
159162 , safe
160163 , servant
@@ -164,27 +167,26 @@ test-suite spec
164167 , transformers
165168 , transformers-compat
166169 , wai
167− , warp
168170
169171 -- Additonal dependencies
170172 build-depends:
171− directory >= 1.2.1.0 && < 1.4
172− , hspec >= 2.4.4 && < 2.6
173− , hspec-wai >= 0.9 && < 0.10
173+ aeson >= 1.3.1.1 && < 1.5
174+ , directory >= 1.2.1.0 && < 1.4
175+ , hspec >= 2.5.1 && < 2.6
176+ , hspec-wai >= 0.9.0 && < 0.10
177+ , QuickCheck >= 2.11.3 && < 2.13
174178 , should-not-typecheck >= 2.1.0 && < 2.2
175− , parsec >= 3.1.11 && < 3.2
176− , QuickCheck >= 2.10.1 && < 2.12
179+ , temporary >= 1.3 && < 1.4
177180 , wai-extra >= 3.0.21.0 && < 3.1
178− , temporary >= 1.2.0.3 && < 1.4
179181
180182 build-tool-depends:
181− hspec-discover:hspec-discover >=2.4.4 && <2.6
183+ hspec-discover:hspec-discover >= 2.5.1 && <2.6
182184
183185test-suite doctests
184186 build-depends:
185187 base
186188 , servant-server
187− , doctest >= 0.13.0 && <0.16
189+ , doctest >= 0.15.0 && <0.17
188190 type: exitcode-stdio-1.0
189191 main-is: test/doctests.hs
190192 buildable: True
servant-server/src/Servant.hs+2−2
@@ -6,7 +6,7 @@ module Servant (
66 -- | For implementing servers for servant APIs.
77 module Servant.Server,
88 -- | Utilities on top of the servant core
9− module Servant.Utils.Links,
9+ module Servant.Links,
1010 module Servant.Utils.StaticFiles,
1111 -- | Useful re-exports
1212 Proxy(..),
@@ -17,5 +17,5 @@ import Control.Monad.Error.Class (throwError)
1717import Data.Proxy
1818import Servant.API
1919import Servant.Server
20−import Servant.Utils.Links
20+import Servant.Links
2121import Servant.Utils.StaticFiles
servant-server/src/Servant/Server/Generic.hsnew file+52
@@ -0,0 +1,52 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeOperators #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+-- | @since 0.14.1
10+module Servant.Server.Generic (
11+ AsServerT,
12+ AsServer,
13+ genericServe,
14+ genericServer,
15+ genericServerT,
16+ ) where
17+
18+import Data.Proxy
19+ (Proxy (..))
20+
21+import Servant.API.Generic
22+import Servant.Server
23+
24+-- | A type that specifies that an API record contains a server implementation.
25+data AsServerT (m :: * -> *)
26+instance GenericMode (AsServerT m) where
27+ type AsServerT m :- api = ServerT api m
28+
29+type AsServer = AsServerT Handler
30+
31+-- | Transform record of routes into a WAI 'Application'.
32+genericServe
33+ :: forall routes.
34+ ( HasServer (ToServantApi routes) '[]
35+ , GenericServant routes AsServer
36+ , Server (ToServantApi routes) ~ ToServant routes AsServer
37+ )
38+ => routes AsServer -> Application
39+genericServe = serve (Proxy :: Proxy (ToServantApi routes)) . genericServer
40+
41+-- | Transform record of endpoints into a 'Server'.
42+genericServer
43+ :: GenericServant routes AsServer
44+ => routes AsServer
45+ -> ToServant routes AsServer
46+genericServer = toServant
47+
48+genericServerT
49+ :: GenericServant routes (AsServerT m)
50+ => routes (AsServerT m)
51+ -> ToServant routes (AsServerT m)
52+genericServerT = toServant
servant-server/src/Servant/Server/Internal/Context.hs+1−1
@@ -18,7 +18,7 @@ import GHC.TypeLits
1818-- | 'Context's are used to pass values to combinators. (They are __not__ meant
1919-- to be used to pass parameters to your handlers, i.e. they should not replace
2020-- any custom 'Control.Monad.Trans.Reader.ReaderT'-monad-stack that you're using
21−-- with 'Servant.Utils.Enter'.) If you don't use combinators that
21+-- with 'hoistServer'.) If you don't use combinators that
2222-- require any context entries, you can just use 'Servant.Server.serve' as always.
2323--
2424-- If you are using combinators that require a non-empty 'Context' you have to
servant-server/src/Servant/Server/StaticFiles.hsnew file+92
@@ -0,0 +1,92 @@
1+{-# LANGUAGE CPP #-}
2+-- | This module defines server-side handlers that lets you serve static files.
3+--
4+-- The most common needs for a web application are covered by
5+-- 'serveDirectoryWebApp`, but the other variants allow you to use
6+-- different `StaticSettings` and 'serveDirectoryWith' even allows you
7+-- to specify arbitrary 'StaticSettings' to be used for serving static files.
8+module Servant.Server.StaticFiles
9+ ( serveDirectoryWebApp
10+ , serveDirectoryWebAppLookup
11+ , serveDirectoryFileServer
12+ , serveDirectoryEmbedded
13+ , serveDirectoryWith
14+ , -- * Deprecated
15+ serveDirectory
16+ ) where
17+
18+import Data.ByteString
19+ (ByteString)
20+import Network.Wai.Application.Static
21+import Servant.API.Raw
22+ (Raw)
23+import Servant.Server
24+ (ServerT, Tagged (..))
25+import System.FilePath
26+ (addTrailingPathSeparator)
27+#if !MIN_VERSION_wai_app_static(3,1,0)
28+import Filesystem.Path.CurrentOS
29+ (decodeString)
30+#endif
31+import WaiAppStatic.Storage.Filesystem
32+ (ETagLookup)
33+
34+-- | Serve anything under the specified directory as a 'Raw' endpoint.
35+--
36+-- @
37+-- type MyApi = "static" :> Raw
38+--
39+-- server :: Server MyApi
40+-- server = serveDirectoryWebApp "\/var\/www"
41+-- @
42+--
43+-- would capture any request to @\/static\/\<something>@ and look for
44+-- @\<something>@ under @\/var\/www@.
45+--
46+-- It will do its best to guess the MIME type for that file, based on the extension,
47+-- and send an appropriate /Content-Type/ header if possible.
48+--
49+-- If your goal is to serve HTML, CSS and Javascript files that use the rest of the API
50+-- as a webapp backend, you will most likely not want the static files to be hidden
51+-- behind a /\/static\// prefix. In that case, remember to put the 'serveDirectoryWebApp'
52+-- handler in the last position, because /servant/ will try to match the handlers
53+-- in order.
54+--
55+-- Corresponds to the `defaultWebAppSettings` `StaticSettings` value.
56+serveDirectoryWebApp :: FilePath -> ServerT Raw m
57+serveDirectoryWebApp = serveDirectoryWith . defaultWebAppSettings . fixPath
58+
59+-- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`.
60+serveDirectoryFileServer :: FilePath -> ServerT Raw m
61+serveDirectoryFileServer = serveDirectoryWith . defaultFileServerSettings . fixPath
62+
63+-- | Same as 'serveDirectoryWebApp', but uses 'webAppSettingsWithLookup'.
64+serveDirectoryWebAppLookup :: ETagLookup -> FilePath -> ServerT Raw m
65+serveDirectoryWebAppLookup etag =
66+ serveDirectoryWith . flip webAppSettingsWithLookup etag . fixPath
67+
68+-- | Uses 'embeddedSettings'.
69+serveDirectoryEmbedded :: [(FilePath, ByteString)] -> ServerT Raw m
70+serveDirectoryEmbedded files = serveDirectoryWith (embeddedSettings files)
71+
72+-- | Alias for 'staticApp'. Lets you serve a directory
73+-- with arbitrary 'StaticSettings'. Useful when you want
74+-- particular settings not covered by the four other
75+-- variants. This is the most flexible method.
76+serveDirectoryWith :: StaticSettings -> ServerT Raw m
77+serveDirectoryWith = Tagged . staticApp
78+
79+-- | Same as 'serveDirectoryFileServer'. It used to be the only
80+-- file serving function in servant pre-0.10 and will be kept
81+-- around for a few versions, but is deprecated.
82+serveDirectory :: FilePath -> ServerT Raw m
83+serveDirectory = serveDirectoryFileServer
84+{-# DEPRECATED serveDirectory "Use serveDirectoryFileServer instead" #-}
85+
86+fixPath :: FilePath -> FilePath
87+fixPath =
88+#if MIN_VERSION_wai_app_static(3,1,0)
89+ addTrailingPathSeparator
90+#else
91+ decodeString . addTrailingPathSeparator
92+#endif
servant-server/src/Servant/Utils/StaticFiles.hs+4−84
@@ -1,86 +1,6 @@
1−{-# LANGUAGE CPP #-}
2−-- | This module defines server-side handlers that lets you serve static files.
3−--
4−-- The most common needs for a web application are covered by
5−-- 'serveDirectoryWebApp`, but the other variants allow you to use
6−-- different `StaticSettings` and 'serveDirectoryWith' even allows you
7−-- to specify arbitrary 'StaticSettings' to be used for serving static files.
81module Servant.Utils.StaticFiles
9− ( serveDirectoryWebApp
10− , serveDirectoryWebAppLookup
11− , serveDirectoryFileServer
12− , serveDirectoryEmbedded
13− , serveDirectoryWith
14− , -- * Deprecated
15− serveDirectory
16− ) where
2+ {-# DEPRECATED "Use Servant.ServerStaticFiles." #-}
3+ ( module Servant.Server.StaticFiles )
4+ where
175
18−import Data.ByteString (ByteString)
19−import Network.Wai.Application.Static
20−import Servant.API.Raw (Raw)
21−import Servant.Server (ServerT, Tagged (..))
22−import System.FilePath (addTrailingPathSeparator)
23−#if !MIN_VERSION_wai_app_static(3,1,0)
24−import Filesystem.Path.CurrentOS (decodeString)
25−#endif
26−import WaiAppStatic.Storage.Filesystem (ETagLookup)
27−
28−-- | Serve anything under the specified directory as a 'Raw' endpoint.
29−--
30−-- @
31−-- type MyApi = "static" :> Raw
32−--
33−-- server :: Server MyApi
34−-- server = serveDirectoryWebApp "\/var\/www"
35−-- @
36−--
37−-- would capture any request to @\/static\/\<something>@ and look for
38−-- @\<something>@ under @\/var\/www@.
39−--
40−-- It will do its best to guess the MIME type for that file, based on the extension,
41−-- and send an appropriate /Content-Type/ header if possible.
42−--
43−-- If your goal is to serve HTML, CSS and Javascript files that use the rest of the API
44−-- as a webapp backend, you will most likely not want the static files to be hidden
45−-- behind a /\/static\// prefix. In that case, remember to put the 'serveDirectoryWebApp'
46−-- handler in the last position, because /servant/ will try to match the handlers
47−-- in order.
48−--
49−-- Corresponds to the `defaultWebAppSettings` `StaticSettings` value.
50−serveDirectoryWebApp :: FilePath -> ServerT Raw m
51−serveDirectoryWebApp = serveDirectoryWith . defaultWebAppSettings . fixPath
52−
53−-- | Same as 'serveDirectoryWebApp', but uses `defaultFileServerSettings`.
54−serveDirectoryFileServer :: FilePath -> ServerT Raw m
55−serveDirectoryFileServer = serveDirectoryWith . defaultFileServerSettings . fixPath
56−
57−-- | Same as 'serveDirectoryWebApp', but uses 'webAppSettingsWithLookup'.
58−serveDirectoryWebAppLookup :: ETagLookup -> FilePath -> ServerT Raw m
59−serveDirectoryWebAppLookup etag =
60− serveDirectoryWith . flip webAppSettingsWithLookup etag . fixPath
61−
62−-- | Uses 'embeddedSettings'.
63−serveDirectoryEmbedded :: [(FilePath, ByteString)] -> ServerT Raw m
64−serveDirectoryEmbedded files = serveDirectoryWith (embeddedSettings files)
65−
66−-- | Alias for 'staticApp'. Lets you serve a directory
67−-- with arbitrary 'StaticSettings'. Useful when you want
68−-- particular settings not covered by the four other
69−-- variants. This is the most flexible method.
70−serveDirectoryWith :: StaticSettings -> ServerT Raw m
71−serveDirectoryWith = Tagged . staticApp
72−
73−-- | Same as 'serveDirectoryFileServer'. It used to be the only
74−-- file serving function in servant pre-0.10 and will be kept
75−-- around for a few versions, but is deprecated.
76−serveDirectory :: FilePath -> ServerT Raw m
77−serveDirectory = serveDirectoryFileServer
78−{-# DEPRECATED serveDirectory "Use serveDirectoryFileServer instead" #-}
79−
80−fixPath :: FilePath -> FilePath
81−fixPath =
82−#if MIN_VERSION_wai_app_static(3,1,0)
83− addTrailingPathSeparator
84−#else
85− decodeString . addTrailingPathSeparator
86−#endif
6+import Servant.Server.StaticFiles
servant-server/test/Servant/Utils/StaticFilesSpec.hs → servant-server/test/Servant/Server/StaticFilesSpec.hsrenamed+25−16
@@ -3,22 +3,31 @@
33{-# LANGUAGE OverloadedStrings #-}
44{-# LANGUAGE TypeOperators #-}
55{-# OPTIONS_GHC -fno-warn-orphans #-}
6−module Servant.Utils.StaticFilesSpec where
7−
8−import Control.Exception (bracket)
9−import Data.Proxy (Proxy (Proxy))
10−import Network.Wai (Application)
11−import System.Directory (createDirectory,
12− getCurrentDirectory,
13− setCurrentDirectory)
14−import System.IO.Temp (withSystemTempDirectory)
15−import Test.Hspec (Spec, around_, describe, it)
16−import Test.Hspec.Wai (get, shouldRespondWith, with)
17−
18−import Servant.API ((:<|>) ((:<|>)), Capture, Get, Raw, (:>), JSON)
19−import Servant.Server (Server, serve)
20−import Servant.ServerSpec (Person (Person))
21−import Servant.Utils.StaticFiles (serveDirectoryFileServer)
6+module Servant.Server.StaticFilesSpec where
7+
8+import Control.Exception
9+ (bracket)
10+import Data.Proxy
11+ (Proxy (Proxy))
12+import Network.Wai
13+ (Application)
14+import System.Directory
15+ (createDirectory, getCurrentDirectory, setCurrentDirectory)
16+import System.IO.Temp
17+ (withSystemTempDirectory)
18+import Test.Hspec
19+ (Spec, around_, describe, it)
20+import Test.Hspec.Wai
21+ (get, shouldRespondWith, with)
22+
23+import Servant.API
24+ ((:<|>) ((:<|>)), (:>), Capture, Get, JSON, Raw)
25+import Servant.Server
26+ (Server, serve)
27+import Servant.Server.StaticFiles
28+ (serveDirectoryFileServer)
29+import Servant.ServerSpec
30+ (Person (Person))
2231
2332type Api =
2433 "dummy_api" :> Capture "person_name" String :> Get '[JSON] Person
servant/CHANGELOG.md+27−5
@@ -1,5 +1,18 @@
11[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
22
3+0.14.1
4+------
5+
6+- Merge in (and slightly refactor) `servant-generic`
7+ (by [Patrick Chilton](https://github.com/chpatrick))
8+ into `servant` (`Servant.API.Generic`),
9+ `servant-client-code` (`Servant.Client.Generic`)
10+ and `servant-server` (`Servant.Server.Generic`).
11+
12+- Deprecate `Servant.Utils.Links`, use `Servant.Links`.
13+
14+- *servant-server* Deprecate `Servant.Utils.StaticUtils`, use `Servant.Server.StaticUtils`.
15+
3160.14
417----
518
@@ -30,16 +43,25 @@
3043- Added `NoFraming` streaming strategy
3144 ([#959](https://github.com/haskell-servant/servant/pull/959))
3245
33−- *servant-client* Free `Client` implementation.
46+- *servant-client-core* Free `Client` implementation.
3447 Useful for testing `HasClient` instances.
3548 ([#920](https://github.com/haskell-servant/servant/pull/920))
3649
37−- *servant-client* Add `hoistClient` to `HasClient`.
50+- *servant-client-core* Add `hoistClient` to `HasClient`.
3851 Just like `hoistServer` allows us to change the monad in which request handlers
3952 of a web application live in, we also have `hoistClient` for changing the monad
40− in which *client functions* live. Read [tutorial section for more information](#link).
53+ in which *client functions* live.
54+ Read [tutorial section for more information](https://haskell-servant.readthedocs.io/en/release-0.14/tutorial/Client.html#changing-the-monad-the-client-functions-live-in).
4155 ([#936](https://github.com/haskell-servant/servant/pull/936))
4256
57+ iF you have own combinators, you'll need to define a new method of
58+ `HasClient` class, for example:
59+
60+ ```haskell
61+ type Client m (MyCombinator :> api) = MyValue :> Client m api
62+ hoistClientMonad pm _ nt cl = hoistClientMonad pm (Proxy :: Proxy api) nt . cl
63+ ```
64+
4365- *servant* Add `safeLink' :: (Link -> a) -> ... -> MkLink endpoint a`,
4466 which allows to create helpers returning something else than `Link`.
4567 ([#968](https://github.com/haskell-servant/servant/pull/968))
@@ -76,10 +98,10 @@
7698- Added tests or enabled tests
7799 ([#975](https://github.com/haskell-servant/servant/pull/975))
78100
79−- Add [pagination cookbook recipe](#link)
101+- Add [pagination cookbook recipe](https://haskell-servant.readthedocs.io/en/release-0.14/cookbook/pagination/Pagination.html)
80102 ([#946](https://github.com/haskell-servant/servant/pull/946))
81103
82−- Add [`servant-flatten` cookbook recipe](#link)
104+- Add [`servant-flatten` "spice" to the structuring api recipe](https://haskell-servant.readthedocs.io/en/release-0.14/cookbook/structuring-apis/StructuringApis.html)
83105 ([#929](https://github.com/haskell-servant/servant/pull/929))
84106
85107- Dependency updates
servant/servant.cabal+34−25
@@ -1,12 +1,16 @@
11name: servant
2−version: 0.14
2+version: 0.14.1
3+x-revision: 1
4+
35synopsis: A family of combinators for defining webservices APIs
6+category: Servant, Web
47description:
58 A family of combinators for defining webservices APIs and serving them
69 .
710 You can learn about the basics in the <http://haskell-servant.readthedocs.org/en/stable/tutorial/index.html tutorial>.
811 .
912 <https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md CHANGELOG>
13+
1014homepage: http://haskell-servant.readthedocs.org/
1115Bug-reports: http://github.com/haskell-servant/servant/issues
1216license: BSD3
@@ -14,7 +18,6 @@ license-file: LICENSE
1418author: Servant Contributors
1519maintainer: haskell-servant-maintainers@googlegroups.com
1620copyright: 2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors
17−category: Servant, Web
1821build-type: Custom
1922cabal-version: >=1.10
2023tested-with:
@@ -22,7 +25,9 @@ tested-with:
2225 GHC==7.10.3
2326 GHC==8.0.2
2427 GHC==8.2.2
25− GHC==8.4.3
28+ GHC==8.4.4
29+ GHC==8.6.1
30+
2631extra-source-files:
2732 include/*.h
2833 CHANGELOG.md
@@ -34,7 +39,7 @@ custom-setup
3439 setup-depends:
3540 base >= 4 && <5,
3641 Cabal,
37− cabal-doctest >= 1.0.2 && <1.1
42+ cabal-doctest >= 1.0.6 && <1.1
3843
3944library
4045 exposed-modules:
@@ -46,6 +51,7 @@ library
4651 Servant.API.Description
4752 Servant.API.Empty
4853 Servant.API.Experimental.Auth
54+ Servant.API.Generic
4955 Servant.API.Header
5056 Servant.API.HttpVersion
5157 Servant.API.Internal.Test.ComprehensiveAPI
@@ -62,6 +68,10 @@ library
6268 Servant.API.Vault
6369 Servant.API.Verbs
6470 Servant.API.WithNamedContext
71+ Servant.Links
72+
73+ -- Deprecated modules, to be removed in late 2019
74+ exposed-modules:
6575 Servant.Utils.Links
6676 Servant.Utils.Enter
6777
@@ -70,32 +80,32 @@ library
7080 --
7181 -- note: mtl lower bound is so low because of GHC-7.8
7282 build-depends:
73− base >= 4.7 && < 4.12
83+ base >= 4.7 && < 4.13
7484 , bytestring >= 0.10.4.0 && < 0.11
7585 , mtl >= 2.1 && < 2.3
7686 , text >= 1.2.3.0 && < 1.3
7787
7888 if !impl(ghc >= 8.0)
7989 build-depends:
80− semigroups >= 0.18.3 && < 0.19
90+ semigroups >= 0.18.4 && < 0.19
8191
8292 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
8393 -- Here can be exceptions if we really need features from the newer versions.
8494 build-depends:
85− base-compat >= 0.9.3 && < 0.11
86− , aeson >= 1.2.3.0 && < 1.5
87− , attoparsec >= 0.13.2.0 && < 0.14
95+ base-compat >= 0.10.1 && < 0.11
96+ , aeson >= 1.3.1.1 && < 1.5
97+ , attoparsec >= 0.13.2.2 && < 0.14
8898 , case-insensitive >= 1.2.0.10 && < 1.3
89− , http-api-data >= 0.3.7.1 && < 0.4
90− , http-media >= 0.7.1.1 && < 0.8
91− , http-types >= 0.12 && < 0.13
99+ , http-api-data >= 0.3.8.1 && < 0.4
100+ , http-media >= 0.7.1.2 && < 0.8
101+ , http-types >= 0.12.1 && < 0.13
92102 , natural-transformation >= 0.4 && < 0.5
93− , mmorph >= 1.1.0 && < 1.2
103+ , mmorph >= 1.1.2 && < 1.2
94104 , tagged >= 0.8.5 && < 0.9
95− , singleton-bool >= 0.1.2.0 && < 0.2
105+ , singleton-bool >= 0.1.4 && < 0.2
96106 , string-conversions >= 0.4.0.1 && < 0.5
97107 , network-uri >= 2.6.1.0 && < 2.7
98− , vault >= 0.3.0.7 && < 0.4
108+ , vault >= 0.3.1.1 && < 0.4
99109
100110 hs-source-dirs: src
101111 default-language: Haskell2010
@@ -133,15 +143,14 @@ test-suite spec
133143 other-modules:
134144 Servant.API.ContentTypesSpec
135145 Servant.API.ResponseHeadersSpec
136− Servant.Utils.LinksSpec
137146 Servant.Utils.EnterSpec
147+ Servant.LinksSpec
138148
139149 -- Dependencies inherited from the library. No need to specify bounds.
140150 build-depends:
141151 base
142152 , base-compat
143153 , aeson
144− , attoparsec
145154 , bytestring
146155 , servant
147156 , string-conversions
@@ -153,23 +162,23 @@ test-suite spec
153162
154163 -- Additonal dependencies
155164 build-depends:
156− aeson-compat >= 0.3.3 && < 0.4
157− , hspec >= 2.4.4 && < 2.6
158− , QuickCheck >= 2.10.1 && < 2.12
159− , quickcheck-instances >= 0.3.16 && < 0.4
165+ aeson-compat >= 0.3.7.1 && < 0.4
166+ , hspec >= 2.5.1 && < 2.6
167+ , QuickCheck >= 2.11.3 && < 2.13
168+ , quickcheck-instances >= 0.3.18 && < 0.4
160169
161170 build-tool-depends:
162− hspec-discover:hspec-discover >= 2.4.4 && < 2.6
171+ hspec-discover:hspec-discover >= 2.5.1 && < 2.6
163172
164173test-suite doctests
165174 build-depends:
166175 base
167176 , servant
168− , doctest >= 0.13.0 && <0.16
177+ , doctest >= 0.15.0 && <0.17
169178
170179 -- We test Links failure with doctest, so we need extra dependencies
171180 build-depends:
172− hspec >= 2.4.4 && < 2.6
181+ hspec >= 2.5.1 && < 2.6
173182
174183 type: exitcode-stdio-1.0
175184 main-is: test/doctests.hs
@@ -180,4 +189,4 @@ test-suite doctests
180189 x-doctest-options: -fdiagnostics-color=never
181190 include-dirs: include
182191 x-doctest-source-dirs: test
183− x-doctest-modules: Servant.Utils.LinksSpec
192+ x-doctest-modules: Servant.LinksSpec
servant/src/Servant/API.hs+3−3
@@ -63,8 +63,8 @@ module Servant.API (
6363 module Servant.API.Experimental.Auth,
6464 -- | General Authentication
6565
66− -- * Utilities
67− module Servant.Utils.Links,
66+ -- * Links
67+ module Servant.Links,
6868 -- | Type-safe internal URIs
6969
7070 -- * Re-exports
@@ -134,7 +134,7 @@ import Servant.API.Verbs
134134 ReflectMethod (reflectMethod), StdMethod (..), Verb)
135135import Servant.API.WithNamedContext
136136 (WithNamedContext)
137−import Servant.Utils.Links
137+import Servant.Links
138138 (HasLink (..), IsElem, IsElem', Link, URI (..), safeLink)
139139import Web.HttpApiData
140140 (FromHttpApiData (..), ToHttpApiData (..))
servant/src/Servant/API/Generic.hsnew file+146
@@ -0,0 +1,146 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE TypeOperators #-}
5+-- | Define servant servers from record types. Generics for the win.
6+--
7+-- The usage is simple, if you only need a collection of routes. First you
8+-- define a record with field types prefixed by a parameter `route`:
9+--
10+-- @
11+-- data Routes route = Routes
12+-- { _get :: route :- Capture "id" Int :> Get '[JSON] String
13+-- , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
14+-- }
15+-- deriving ('Generic')
16+-- @
17+--
18+-- You can get a 'Proxy' of the server using
19+--
20+-- @
21+-- api :: Proxy (ToServantApi Routes)
22+-- api = genericApi (Proxy :: Proxy Routes)
23+-- @
24+--
25+-- Using 'genericApi' is better as it checks that instances exists,
26+-- i.e. you get better error messages than simply using 'Proxy' value.
27+--
28+-- __Note:__ in 0.14 series this module isn't re-exported from 'Servant.API'.
29+--
30+-- "Servant.API.Generic" is based on @servant-generic@ package by
31+-- [Patrick Chilton](https://github.com/chpatrick)
32+--
33+-- @since 0.14.1
34+module Servant.API.Generic (
35+ GenericMode (..),
36+ GenericServant,
37+ ToServant,
38+ toServant,
39+ fromServant,
40+ -- * AsApi
41+ AsApi,
42+ ToServantApi,
43+ genericApi,
44+ -- * Utility
45+ GServantProduct,
46+ -- * re-exports
47+ Generic (Rep),
48+ ) where
49+
50+-- Based on servant-generic licensed under MIT License
51+--
52+-- Copyright (c) 2017 Patrick Chilton
53+--
54+-- Permission is hereby granted, free of charge, to any person obtaining a copy
55+-- of this software and associated documentation files (the "Software"), to deal
56+-- in the Software without restriction, including without limitation the rights
57+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58+-- copies of the Software, and to permit persons to whom the Software is
59+-- furnished to do so, subject to the following conditions:
60+--
61+-- The above copyright notice and this permission notice shall be included in all
62+-- copies or substantial portions of the Software.
63+--
64+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
69+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
70+-- SOFTWARE.
71+
72+import Data.Proxy
73+ (Proxy (..))
74+import GHC.Generics
75+ ((:*:) (..), Generic (..), K1 (..), M1 (..))
76+
77+import Servant.API.Alternative
78+
79+-- | A constraint alias, for work with 'mode' and 'routes'.
80+type GenericServant routes mode = (GenericMode mode, Generic (routes mode), GServantProduct (Rep (routes mode)))
81+
82+-- | A class with a type family that applies an appropriate type family to the @api@
83+-- parameter. For example, 'AsApi' will leave @api@ untouched, while
84+-- @'AsServerT' m@ will produce @'ServerT' api m@.
85+class GenericMode mode where
86+ type mode :- api :: *
87+
88+infixl 0 :-
89+
90+-- | Turns a generic product type into a tree of `:<|>` combinators.
91+type ToServant routes mode = GToServant (Rep (routes mode))
92+
93+type ToServantApi routes = ToServant routes AsApi
94+
95+-- | See `ToServant`, but at value-level.
96+toServant
97+ :: GenericServant routes mode
98+ => routes mode -> ToServant routes mode
99+toServant = gtoServant . from
100+
101+-- | Inverse of `toServant`.
102+--
103+-- This can be used to turn 'generated' values such as client functions into records.
104+--
105+-- You may need to provide a type signature for the /output/ type (your record type).
106+fromServant
107+ :: GenericServant routes mode
108+ => ToServant routes mode -> routes mode
109+fromServant = to . gfromServant
110+
111+-- | A type that specifies that an API record contains an API definition. Only useful at type-level.
112+data AsApi
113+instance GenericMode AsApi where
114+ type AsApi :- api = api
115+
116+-- | Get a 'Proxy' of an API type.
117+genericApi
118+ :: GenericServant routes AsApi
119+ => Proxy routes
120+ -> Proxy (ToServantApi routes)
121+genericApi _ = Proxy
122+
123+-------------------------------------------------------------------------------
124+-- Class
125+-------------------------------------------------------------------------------
126+
127+
128+class GServantProduct f where
129+ type GToServant f
130+ gtoServant :: f p -> GToServant f
131+ gfromServant :: GToServant f -> f p
132+
133+instance GServantProduct f => GServantProduct (M1 i c f) where
134+ type GToServant (M1 i c f) = GToServant f
135+ gtoServant = gtoServant . unM1
136+ gfromServant = M1 . gfromServant
137+
138+instance (GServantProduct l, GServantProduct r) => GServantProduct (l :*: r) where
139+ type GToServant (l :*: r) = GToServant l :<|> GToServant r
140+ gtoServant (l :*: r) = gtoServant l :<|> gtoServant r
141+ gfromServant (l :<|> r) = gfromServant l :*: gfromServant r
142+
143+instance GServantProduct (K1 i c) where
144+ type GToServant (K1 i c) = c
145+ gtoServant = unK1
146+ gfromServant = K1
servant/src/Servant/API/Verbs.hs+1−1
@@ -58,7 +58,7 @@ type Patch = Verb 'PATCH 200
5858--
5959-- If the resource cannot be created immediately, use 'PostAccepted'.
6060--
61−-- Consider using 'Servant.Utils.Links.safeLink' for the @Location@ header
61+-- Consider using 'Servant.Links.safeLink' for the @Location@ header
6262-- field.
6363
6464-- | 'POST' with 201 status code.
servant/src/Servant/Links.hsnew file+573
@@ -0,0 +1,573 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FunctionalDependencies #-}
6+{-# LANGUAGE PolyKinds #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE TypeOperators #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+{-# OPTIONS_HADDOCK not-home #-}
12+
13+-- | Type safe generation of internal links.
14+--
15+-- Given an API with a few endpoints:
16+--
17+-- >>> :set -XDataKinds -XTypeFamilies -XTypeOperators
18+-- >>> import Servant.API
19+-- >>> import Servant.Links
20+-- >>> import Data.Proxy
21+-- >>>
22+-- >>> type Hello = "hello" :> Get '[JSON] Int
23+-- >>> type Bye = "bye" :> QueryParam "name" String :> Delete '[JSON] NoContent
24+-- >>> type API = Hello :<|> Bye
25+-- >>> let api = Proxy :: Proxy API
26+--
27+-- It is possible to generate links that are guaranteed to be within 'API' with
28+-- 'safeLink'. The first argument to 'safeLink' is a type representing the API
29+-- you would like to restrict links to. The second argument is the destination
30+-- endpoint you would like the link to point to, this will need to end with a
31+-- verb like GET or POST. Further arguments may be required depending on the
32+-- type of the endpoint. If everything lines up you will get a 'Link' out the
33+-- other end.
34+--
35+-- You may omit 'QueryParam's and the like should you not want to provide them,
36+-- but types which form part of the URL path like 'Capture' must be included.
37+-- The reason you may want to omit 'QueryParam's is that safeLink is a bit
38+-- magical: if parameters are included that could take input it will return a
39+-- function that accepts that input and generates a link. This is best shown
40+-- with an example. Here, a link is generated with no parameters:
41+--
42+-- >>> let hello = Proxy :: Proxy ("hello" :> Get '[JSON] Int)
43+-- >>> toUrlPiece (safeLink api hello :: Link)
44+-- "hello"
45+--
46+-- If the API has an endpoint with parameters then we can generate links with
47+-- or without those:
48+--
49+-- >>> let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete '[JSON] NoContent)
50+-- >>> toUrlPiece $ safeLink api with (Just "Hubert")
51+-- "bye?name=Hubert"
52+--
53+-- >>> let without = Proxy :: Proxy ("bye" :> Delete '[JSON] NoContent)
54+-- >>> toUrlPiece $ safeLink api without
55+-- "bye"
56+--
57+-- If you would like create a helper for generating links only within that API,
58+-- you can partially apply safeLink if you specify a correct type signature
59+-- like so:
60+--
61+-- >>> :set -XConstraintKinds
62+-- >>> :{
63+-- >>> let apiLink :: (IsElem endpoint API, HasLink endpoint)
64+-- >>> => Proxy endpoint -> MkLink endpoint Link
65+-- >>> apiLink = safeLink api
66+-- >>> :}
67+--
68+-- `safeLink'` allows to make specialise the output:
69+--
70+-- >>> safeLink' toUrlPiece api without
71+-- "bye"
72+--
73+-- >>> :{
74+-- >>> let apiTextLink :: (IsElem endpoint API, HasLink endpoint)
75+-- >>> => Proxy endpoint -> MkLink endpoint Text
76+-- >>> apiTextLink = safeLink' toUrlPiece api
77+-- >>> :}
78+--
79+-- >>> apiTextLink without
80+-- "bye"
81+--
82+-- Attempting to construct a link to an endpoint that does not exist in api
83+-- will result in a type error like this:
84+--
85+-- >>> let bad_link = Proxy :: Proxy ("hello" :> Delete '[JSON] NoContent)
86+-- >>> safeLink api bad_link
87+-- ...
88+-- ...Could not deduce...
89+-- ...
90+--
91+-- This error is essentially saying that the type family couldn't find
92+-- bad_link under api after trying the open (but empty) type family
93+-- `IsElem'` as a last resort.
94+--
95+-- @since 0.14.1
96+module Servant.Links (
97+ module Servant.API.TypeLevel,
98+
99+ -- * Building and using safe links
100+ --
101+ -- | Note that 'URI' is from the "Network.URI" module in the @network-uri@ package.
102+ safeLink
103+ , safeLink'
104+ , allLinks
105+ , allLinks'
106+ , URI(..)
107+ -- * Generics
108+ , AsLink
109+ , fieldLink
110+ , fieldLink'
111+ , allFieldLinks
112+ , allFieldLinks'
113+ -- * Adding custom types
114+ , HasLink(..)
115+ , Link
116+ , linkURI
117+ , linkURI'
118+ , LinkArrayElementStyle (..)
119+ -- ** Link accessors
120+ , Param (..)
121+ , linkSegments
122+ , linkQueryParams
123+) where
124+
125+import Data.List
126+import Data.Proxy
127+ (Proxy (..))
128+import Data.Semigroup
129+ ((<>))
130+import Data.Singletons.Bool
131+ (SBool (..), SBoolI (..))
132+import qualified Data.Text as Text
133+import qualified Data.Text.Encoding as TE
134+import Data.Type.Bool
135+ (If)
136+import GHC.TypeLits
137+ (KnownSymbol, symbolVal)
138+import Network.URI
139+ (URI (..), escapeURIString, isUnreserved)
140+import Prelude ()
141+import Prelude.Compat
142+
143+import Servant.API.Alternative
144+ ((:<|>) ((:<|>)))
145+import Servant.API.BasicAuth
146+ (BasicAuth)
147+import Servant.API.Capture
148+ (Capture', CaptureAll)
149+import Servant.API.Description
150+ (Description, Summary)
151+import Servant.API.Empty
152+ (EmptyAPI (..))
153+import Servant.API.Experimental.Auth
154+ (AuthProtect)
155+import Servant.API.Generic
156+import Servant.API.Header
157+ (Header')
158+import Servant.API.HttpVersion
159+ (HttpVersion)
160+import Servant.API.IsSecure
161+ (IsSecure)
162+import Servant.API.Modifiers
163+ (FoldRequired)
164+import Servant.API.QueryParam
165+ (QueryFlag, QueryParam', QueryParams)
166+import Servant.API.Raw
167+ (Raw)
168+import Servant.API.RemoteHost
169+ (RemoteHost)
170+import Servant.API.ReqBody
171+ (ReqBody')
172+import Servant.API.Stream
173+ (Stream)
174+import Servant.API.Sub
175+ (type (:>))
176+import Servant.API.TypeLevel
177+import Servant.API.Vault
178+ (Vault)
179+import Servant.API.Verbs
180+ (Verb)
181+import Servant.API.WithNamedContext
182+ (WithNamedContext)
183+import Web.HttpApiData
184+
185+-- | A safe link datatype.
186+-- The only way of constructing a 'Link' is using 'safeLink', which means any
187+-- 'Link' is guaranteed to be part of the mentioned API.
188+data Link = Link
189+ { _segments :: [Escaped]
190+ , _queryParams :: [Param]
191+ } deriving Show
192+
193+newtype Escaped = Escaped String
194+
195+escaped :: String -> Escaped
196+escaped = Escaped . escapeURIString isUnreserved
197+
198+getEscaped :: Escaped -> String
199+getEscaped (Escaped s) = s
200+
201+instance Show Escaped where
202+ showsPrec d (Escaped s) = showsPrec d s
203+ show (Escaped s) = show s
204+
205+linkSegments :: Link -> [String]
206+linkSegments = map getEscaped . _segments
207+
208+linkQueryParams :: Link -> [Param]
209+linkQueryParams = _queryParams
210+
211+instance ToHttpApiData Link where
212+ toHeader = TE.encodeUtf8 . toUrlPiece
213+ toUrlPiece l =
214+ let uri = linkURI l
215+ in Text.pack $ uriPath uri ++ uriQuery uri
216+
217+-- | Query parameter.
218+data Param
219+ = SingleParam String Text.Text
220+ | ArrayElemParam String Text.Text
221+ | FlagParam String
222+ deriving Show
223+
224+addSegment :: Escaped -> Link -> Link
225+addSegment seg l = l { _segments = _segments l <> [seg] }
226+
227+addQueryParam :: Param -> Link -> Link
228+addQueryParam qp l =
229+ l { _queryParams = _queryParams l <> [qp] }
230+
231+-- | Transform 'Link' into 'URI'.
232+--
233+-- >>> type API = "something" :> Get '[JSON] Int
234+-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)
235+-- something
236+--
237+-- >>> type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int
238+-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
239+-- sum?x[]=1&x[]=2&x[]=3
240+--
241+-- >>> type API = "foo/bar" :> Get '[JSON] Int
242+-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)
243+-- foo%2Fbar
244+--
245+-- >>> type SomeRoute = "abc" :> Capture "email" String :> Put '[JSON] ()
246+-- >>> let someRoute = Proxy :: Proxy SomeRoute
247+-- >>> safeLink someRoute someRoute "test@example.com"
248+-- Link {_segments = ["abc","test%40example.com"], _queryParams = []}
249+--
250+-- >>> linkURI $ safeLink someRoute someRoute "test@example.com"
251+-- abc/test%40example.com
252+--
253+linkURI :: Link -> URI
254+linkURI = linkURI' LinkArrayElementBracket
255+
256+-- | How to encode array query elements.
257+data LinkArrayElementStyle
258+ = LinkArrayElementBracket -- ^ @foo[]=1&foo[]=2@
259+ | LinkArrayElementPlain -- ^ @foo=1&foo=2@
260+ deriving (Eq, Ord, Show, Enum, Bounded)
261+
262+-- | Configurable 'linkURI'.
263+--
264+-- >>> type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int
265+-- >>> linkURI' LinkArrayElementBracket $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
266+-- sum?x[]=1&x[]=2&x[]=3
267+--
268+-- >>> linkURI' LinkArrayElementPlain $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
269+-- sum?x=1&x=2&x=3
270+--
271+linkURI' :: LinkArrayElementStyle -> Link -> URI
272+linkURI' addBrackets (Link segments q_params) =
273+ URI mempty -- No scheme (relative)
274+ Nothing -- Or authority (relative)
275+ (intercalate "/" $ map getEscaped segments)
276+ (makeQueries q_params) mempty
277+ where
278+ makeQueries :: [Param] -> String
279+ makeQueries [] = ""
280+ makeQueries xs =
281+ "?" <> intercalate "&" (fmap makeQuery xs)
282+
283+ makeQuery :: Param -> String
284+ makeQuery (ArrayElemParam k v) = escape k <> style <> escape (Text.unpack v)
285+ makeQuery (SingleParam k v) = escape k <> "=" <> escape (Text.unpack v)
286+ makeQuery (FlagParam k) = escape k
287+
288+ style = case addBrackets of
289+ LinkArrayElementBracket -> "[]="
290+ LinkArrayElementPlain -> "="
291+
292+escape :: String -> String
293+escape = escapeURIString isUnreserved
294+
295+-- | Create a valid (by construction) relative URI with query params.
296+--
297+-- This function will only typecheck if `endpoint` is part of the API `api`
298+safeLink
299+ :: forall endpoint api. (IsElem endpoint api, HasLink endpoint)
300+ => Proxy api -- ^ The whole API that this endpoint is a part of
301+ -> Proxy endpoint -- ^ The API endpoint you would like to point to
302+ -> MkLink endpoint Link
303+safeLink = safeLink' id
304+
305+-- | More general 'safeLink'.
306+--
307+safeLink'
308+ :: forall endpoint api a. (IsElem endpoint api, HasLink endpoint)
309+ => (Link -> a)
310+ -> Proxy api -- ^ The whole API that this endpoint is a part of
311+ -> Proxy endpoint -- ^ The API endpoint you would like to point to
312+ -> MkLink endpoint a
313+safeLink' toA _ endpoint = toLink toA endpoint (Link mempty mempty)
314+
315+-- | Create all links in an API.
316+--
317+-- Note that the @api@ type must be restricted to the endpoints that have
318+-- valid links to them.
319+--
320+-- >>> type API = "foo" :> Capture "name" Text :> Get '[JSON] Text :<|> "bar" :> Capture "name" Int :> Get '[JSON] Double
321+-- >>> let fooLink :<|> barLink = allLinks (Proxy :: Proxy API)
322+-- >>> :t fooLink
323+-- fooLink :: Text -> Link
324+-- >>> :t barLink
325+-- barLink :: Int -> Link
326+--
327+-- Note: nested APIs don't work well with this approach
328+--
329+-- >>> :kind! MkLink (Capture "nest" Char :> (Capture "x" Int :> Get '[JSON] Int :<|> Capture "y" Double :> Get '[JSON] Double)) Link
330+-- MkLink (Capture "nest" Char :> (Capture "x" Int :> Get '[JSON] Int :<|> Capture "y" Double :> Get '[JSON] Double)) Link :: *
331+-- = Char -> (Int -> Link) :<|> (Double -> Link)
332+allLinks
333+ :: forall api. HasLink api
334+ => Proxy api
335+ -> MkLink api Link
336+allLinks = allLinks' id
337+
338+-- | More general 'allLinks'. See `safeLink'`.
339+allLinks'
340+ :: forall api a. HasLink api
341+ => (Link -> a)
342+ -> Proxy api
343+ -> MkLink api a
344+allLinks' toA api = toLink toA api (Link mempty mempty)
345+
346+-------------------------------------------------------------------------------
347+-- Generics
348+-------------------------------------------------------------------------------
349+
350+-- | Given an API record field, create a link for that route. Only the field's
351+-- type is used.
352+--
353+-- @
354+-- data Record route = Record
355+-- { _get :: route :- Capture "id" Int :> Get '[JSON] String
356+-- , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
357+-- }
358+-- deriving ('Generic')
359+--
360+-- getLink :: Int -> Link
361+-- getLink = 'fieldLink' _get
362+-- @
363+--
364+-- @since 0.14.1
365+fieldLink
366+ :: ( IsElem endpoint (ToServantApi routes), HasLink endpoint
367+ , GenericServant routes AsApi
368+ )
369+ => (routes AsApi -> endpoint)
370+ -> MkLink endpoint Link
371+fieldLink = fieldLink' id
372+
373+-- | More general version of 'fieldLink'
374+--
375+-- @since 0.14.1
376+fieldLink'
377+ :: forall routes endpoint a.
378+ ( IsElem endpoint (ToServantApi routes), HasLink endpoint
379+ , GenericServant routes AsApi
380+ )
381+ => (Link -> a)
382+ -> (routes AsApi -> endpoint)
383+ -> MkLink endpoint a
384+fieldLink' toA _ = safeLink' toA (genericApi (Proxy :: Proxy routes)) (Proxy :: Proxy endpoint)
385+
386+-- | A type that specifies that an API record contains a set of links.
387+--
388+-- @since 0.14.1
389+data AsLink (a :: *)
390+instance GenericMode (AsLink a) where
391+ type (AsLink a) :- api = MkLink api a
392+
393+-- | Get all links as a record.
394+--
395+-- @since 0.14.1
396+allFieldLinks
397+ :: ( HasLink (ToServantApi routes)
398+ , GenericServant routes (AsLink Link)
399+ , ToServant routes (AsLink Link) ~ MkLink (ToServantApi routes) Link
400+ )
401+ => routes (AsLink Link)
402+allFieldLinks = allFieldLinks' id
403+
404+-- | More general version of 'allFieldLinks'.
405+--
406+-- @since 0.14.1
407+allFieldLinks'
408+ :: forall routes a.
409+ ( HasLink (ToServantApi routes)
410+ , GenericServant routes (AsLink a)
411+ , ToServant routes (AsLink a) ~ MkLink (ToServantApi routes) a
412+ )
413+ => (Link -> a)
414+ -> routes (AsLink a)
415+allFieldLinks' toA
416+ = fromServant
417+ $ allLinks' toA (Proxy :: Proxy (ToServantApi routes))
418+
419+-------------------------------------------------------------------------------
420+-- HasLink
421+-------------------------------------------------------------------------------
422+
423+-- | Construct a toLink for an endpoint.
424+class HasLink endpoint where
425+ type MkLink endpoint (a :: *)
426+ toLink
427+ :: (Link -> a)
428+ -> Proxy endpoint -- ^ The API endpoint you would like to point to
429+ -> Link
430+ -> MkLink endpoint a
431+
432+-- Naked symbol instance
433+instance (KnownSymbol sym, HasLink sub) => HasLink (sym :> sub) where
434+ type MkLink (sym :> sub) a = MkLink sub a
435+ toLink toA _ =
436+ toLink toA (Proxy :: Proxy sub) . addSegment (escaped seg)
437+ where
438+ seg = symbolVal (Proxy :: Proxy sym)
439+
440+-- QueryParam instances
441+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub, SBoolI (FoldRequired mods))
442+ => HasLink (QueryParam' mods sym v :> sub)
443+ where
444+ type MkLink (QueryParam' mods sym v :> sub) a = If (FoldRequired mods) v (Maybe v) -> MkLink sub a
445+ toLink toA _ l mv =
446+ toLink toA (Proxy :: Proxy sub) $
447+ case sbool :: SBool (FoldRequired mods) of
448+ STrue -> (addQueryParam . SingleParam k . toQueryParam) mv l
449+ SFalse -> maybe id (addQueryParam . SingleParam k . toQueryParam) mv l
450+ where
451+ k :: String
452+ k = symbolVal (Proxy :: Proxy sym)
453+
454+instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
455+ => HasLink (QueryParams sym v :> sub)
456+ where
457+ type MkLink (QueryParams sym v :> sub) a = [v] -> MkLink sub a
458+ toLink toA _ l =
459+ toLink toA (Proxy :: Proxy sub) .
460+ foldl' (\l' v -> addQueryParam (ArrayElemParam k (toQueryParam v)) l') l
461+ where
462+ k = symbolVal (Proxy :: Proxy sym)
463+
464+instance (KnownSymbol sym, HasLink sub)
465+ => HasLink (QueryFlag sym :> sub)
466+ where
467+ type MkLink (QueryFlag sym :> sub) a = Bool -> MkLink sub a
468+ toLink toA _ l False =
469+ toLink toA (Proxy :: Proxy sub) l
470+ toLink toA _ l True =
471+ toLink toA (Proxy :: Proxy sub) $ addQueryParam (FlagParam k) l
472+ where
473+ k = symbolVal (Proxy :: Proxy sym)
474+
475+-- :<|> instance - Generate all links at once
476+instance (HasLink a, HasLink b) => HasLink (a :<|> b) where
477+ type MkLink (a :<|> b) r = MkLink a r :<|> MkLink b r
478+ toLink toA _ l = toLink toA (Proxy :: Proxy a) l :<|> toLink toA (Proxy :: Proxy b) l
479+
480+-- Misc instances
481+instance HasLink sub => HasLink (ReqBody' mods ct a :> sub) where
482+ type MkLink (ReqBody' mods ct a :> sub) r = MkLink sub r
483+ toLink toA _ = toLink toA (Proxy :: Proxy sub)
484+
485+instance (ToHttpApiData v, HasLink sub)
486+ => HasLink (Capture' mods sym v :> sub)
487+ where
488+ type MkLink (Capture' mods sym v :> sub) a = v -> MkLink sub a
489+ toLink toA _ l v =
490+ toLink toA (Proxy :: Proxy sub) $
491+ addSegment (escaped . Text.unpack $ toUrlPiece v) l
492+
493+instance (ToHttpApiData v, HasLink sub)
494+ => HasLink (CaptureAll sym v :> sub)
495+ where
496+ type MkLink (CaptureAll sym v :> sub) a = [v] -> MkLink sub a
497+ toLink toA _ l vs = toLink toA (Proxy :: Proxy sub) $
498+ foldl' (flip $ addSegment . escaped . Text.unpack . toUrlPiece) l vs
499+
500+instance HasLink sub => HasLink (Header' mods sym (a :: *) :> sub) where
501+ type MkLink (Header' mods sym a :> sub) r = MkLink sub r
502+ toLink = simpleToLink (Proxy :: Proxy sub)
503+
504+instance HasLink sub => HasLink (Vault :> sub) where
505+ type MkLink (Vault :> sub) a = MkLink sub a
506+ toLink = simpleToLink (Proxy :: Proxy sub)
507+
508+instance HasLink sub => HasLink (Description s :> sub) where
509+ type MkLink (Description s :> sub) a = MkLink sub a
510+ toLink = simpleToLink (Proxy :: Proxy sub)
511+
512+instance HasLink sub => HasLink (Summary s :> sub) where
513+ type MkLink (Summary s :> sub) a = MkLink sub a
514+ toLink = simpleToLink (Proxy :: Proxy sub)
515+
516+instance HasLink sub => HasLink (HttpVersion :> sub) where
517+ type MkLink (HttpVersion:> sub) a = MkLink sub a
518+ toLink = simpleToLink (Proxy :: Proxy sub)
519+
520+instance HasLink sub => HasLink (IsSecure :> sub) where
521+ type MkLink (IsSecure :> sub) a = MkLink sub a
522+ toLink = simpleToLink (Proxy :: Proxy sub)
523+
524+instance HasLink sub => HasLink (WithNamedContext name context sub) where
525+ type MkLink (WithNamedContext name context sub) a = MkLink sub a
526+ toLink toA _ = toLink toA (Proxy :: Proxy sub)
527+
528+instance HasLink sub => HasLink (RemoteHost :> sub) where
529+ type MkLink (RemoteHost :> sub) a = MkLink sub a
530+ toLink = simpleToLink (Proxy :: Proxy sub)
531+
532+instance HasLink sub => HasLink (BasicAuth realm a :> sub) where
533+ type MkLink (BasicAuth realm a :> sub) r = MkLink sub r
534+ toLink = simpleToLink (Proxy :: Proxy sub)
535+
536+instance HasLink EmptyAPI where
537+ type MkLink EmptyAPI a = EmptyAPI
538+ toLink _ _ _ = EmptyAPI
539+
540+-- Verb (terminal) instances
541+instance HasLink (Verb m s ct a) where
542+ type MkLink (Verb m s ct a) r = r
543+ toLink toA _ = toA
544+
545+instance HasLink Raw where
546+ type MkLink Raw a = a
547+ toLink toA _ = toA
548+
549+instance HasLink (Stream m status fr ct a) where
550+ type MkLink (Stream m status fr ct a) r = r
551+ toLink toA _ = toA
552+
553+-- AuthProtext instances
554+instance HasLink sub => HasLink (AuthProtect tag :> sub) where
555+ type MkLink (AuthProtect tag :> sub) a = MkLink sub a
556+ toLink = simpleToLink (Proxy :: Proxy sub)
557+
558+-- | Helper for implemneting 'toLink' for combinators not affecting link
559+-- structure.
560+simpleToLink
561+ :: forall sub a combinator.
562+ (HasLink sub, MkLink sub a ~ MkLink (combinator :> sub) a)
563+ => Proxy sub
564+ -> (Link -> a)
565+ -> Proxy (combinator :> sub)
566+ -> Link
567+ -> MkLink (combinator :> sub) a
568+simpleToLink _ toA _ = toLink toA (Proxy :: Proxy sub)
569+
570+
571+-- $setup
572+-- >>> import Servant.API
573+-- >>> import Data.Text (Text)
servant/src/Servant/Utils/Links.hs+4−485
@@ -1,487 +1,6 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE PolyKinds #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE TypeOperators #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−{-# OPTIONS_HADDOCK not-home #-}
12−
13−-- | Type safe generation of internal links.
14−--
15−-- Given an API with a few endpoints:
16−--
17−-- >>> :set -XDataKinds -XTypeFamilies -XTypeOperators
18−-- >>> import Servant.API
19−-- >>> import Servant.Utils.Links
20−-- >>> import Data.Proxy
21−-- >>>
22−-- >>> type Hello = "hello" :> Get '[JSON] Int
23−-- >>> type Bye = "bye" :> QueryParam "name" String :> Delete '[JSON] NoContent
24−-- >>> type API = Hello :<|> Bye
25−-- >>> let api = Proxy :: Proxy API
26−--
27−-- It is possible to generate links that are guaranteed to be within 'API' with
28−-- 'safeLink'. The first argument to 'safeLink' is a type representing the API
29−-- you would like to restrict links to. The second argument is the destination
30−-- endpoint you would like the link to point to, this will need to end with a
31−-- verb like GET or POST. Further arguments may be required depending on the
32−-- type of the endpoint. If everything lines up you will get a 'Link' out the
33−-- other end.
34−--
35−-- You may omit 'QueryParam's and the like should you not want to provide them,
36−-- but types which form part of the URL path like 'Capture' must be included.
37−-- The reason you may want to omit 'QueryParam's is that safeLink is a bit
38−-- magical: if parameters are included that could take input it will return a
39−-- function that accepts that input and generates a link. This is best shown
40−-- with an example. Here, a link is generated with no parameters:
41−--
42−-- >>> let hello = Proxy :: Proxy ("hello" :> Get '[JSON] Int)
43−-- >>> toUrlPiece (safeLink api hello :: Link)
44−-- "hello"
45−--
46−-- If the API has an endpoint with parameters then we can generate links with
47−-- or without those:
48−--
49−-- >>> let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete '[JSON] NoContent)
50−-- >>> toUrlPiece $ safeLink api with (Just "Hubert")
51−-- "bye?name=Hubert"
52−--
53−-- >>> let without = Proxy :: Proxy ("bye" :> Delete '[JSON] NoContent)
54−-- >>> toUrlPiece $ safeLink api without
55−-- "bye"
56−--
57−-- If you would like create a helper for generating links only within that API,
58−-- you can partially apply safeLink if you specify a correct type signature
59−-- like so:
60−--
61−-- >>> :set -XConstraintKinds
62−-- >>> :{
63−-- >>> let apiLink :: (IsElem endpoint API, HasLink endpoint)
64−-- >>> => Proxy endpoint -> MkLink endpoint Link
65−-- >>> apiLink = safeLink api
66−-- >>> :}
67−--
68−-- `safeLink'` allows to make specialise the output:
69−--
70−-- >>> safeLink' toUrlPiece api without
71−-- "bye"
72−--
73−-- >>> :{
74−-- >>> let apiTextLink :: (IsElem endpoint API, HasLink endpoint)
75−-- >>> => Proxy endpoint -> MkLink endpoint Text
76−-- >>> apiTextLink = safeLink' toUrlPiece api
77−-- >>> :}
78−--
79−-- >>> apiTextLink without
80−-- "bye"
81−--
82−-- Attempting to construct a link to an endpoint that does not exist in api
83−-- will result in a type error like this:
84−--
85−-- >>> let bad_link = Proxy :: Proxy ("hello" :> Delete '[JSON] NoContent)
86−-- >>> safeLink api bad_link
87−-- ...
88−-- ...Could not deduce...
89−-- ...
90−--
91−-- This error is essentially saying that the type family couldn't find
92−-- bad_link under api after trying the open (but empty) type family
93−-- `IsElem'` as a last resort.
94−module Servant.Utils.Links (
95− module Servant.API.TypeLevel,
96−
97− -- * Building and using safe links
98− --
99− -- | Note that 'URI' is from the "Network.URI" module in the @network-uri@ package.
100− safeLink
101− , safeLink'
102− , allLinks
103− , allLinks'
104− , URI(..)
105− -- * Adding custom types
106− , HasLink(..)
107− , Link
108− , linkURI
109− , linkURI'
110− , LinkArrayElementStyle (..)
111− -- ** Link accessors
112− , Param (..)
113− , linkSegments
114− , linkQueryParams
115−) where
116−
117−import Data.List
118−import Data.Proxy
119− (Proxy (..))
120−import Data.Semigroup
121− ((<>))
122−import Data.Singletons.Bool
123− (SBool (..), SBoolI (..))
124−import qualified Data.Text as Text
125−import qualified Data.Text.Encoding as TE
126−import Data.Type.Bool
127− (If)
128−import GHC.TypeLits
129− (KnownSymbol, symbolVal)
130−import Network.URI
131− (URI (..), escapeURIString, isUnreserved)
132−import Prelude ()
133−import Prelude.Compat
134−
135−import Servant.API.Alternative
136− ((:<|>) ((:<|>)))
137−import Servant.API.BasicAuth
138− (BasicAuth)
139−import Servant.API.Capture
140− (Capture', CaptureAll)
141−import Servant.API.Description
142− (Description, Summary)
143−import Servant.API.Empty
144− (EmptyAPI (..))
145−import Servant.API.Experimental.Auth
146− (AuthProtect)
147−import Servant.API.Header
148− (Header')
149−import Servant.API.HttpVersion
150− (HttpVersion)
151−import Servant.API.IsSecure
152− (IsSecure)
153−import Servant.API.Modifiers
154− (FoldRequired)
155−import Servant.API.QueryParam
156− (QueryFlag, QueryParam', QueryParams)
157−import Servant.API.Raw
158− (Raw)
159−import Servant.API.RemoteHost
160− (RemoteHost)
161−import Servant.API.ReqBody
162− (ReqBody')
163−import Servant.API.Stream
164− (Stream)
165−import Servant.API.Sub
166− (type (:>))
167−import Servant.API.TypeLevel
168−import Servant.API.Vault
169− (Vault)
170−import Servant.API.Verbs
171− (Verb)
172−import Servant.API.WithNamedContext
173− (WithNamedContext)
174−import Web.HttpApiData
175−
176−-- | A safe link datatype.
177−-- The only way of constructing a 'Link' is using 'safeLink', which means any
178−-- 'Link' is guaranteed to be part of the mentioned API.
179−data Link = Link
180− { _segments :: [Escaped]
181− , _queryParams :: [Param]
182− } deriving Show
183−
184−newtype Escaped = Escaped String
185−
186−escaped :: String -> Escaped
187−escaped = Escaped . escapeURIString isUnreserved
188−
189−getEscaped :: Escaped -> String
190−getEscaped (Escaped s) = s
191−
192−instance Show Escaped where
193− showsPrec d (Escaped s) = showsPrec d s
194− show (Escaped s) = show s
195−
196−linkSegments :: Link -> [String]
197−linkSegments = map getEscaped . _segments
198−
199−linkQueryParams :: Link -> [Param]
200−linkQueryParams = _queryParams
201−
202−instance ToHttpApiData Link where
203− toHeader = TE.encodeUtf8 . toUrlPiece
204− toUrlPiece l =
205− let uri = linkURI l
206− in Text.pack $ uriPath uri ++ uriQuery uri
207−
208−-- | Query parameter.
209−data Param
210− = SingleParam String Text.Text
211− | ArrayElemParam String Text.Text
212− | FlagParam String
213− deriving Show
214−
215−addSegment :: Escaped -> Link -> Link
216−addSegment seg l = l { _segments = _segments l <> [seg] }
217−
218−addQueryParam :: Param -> Link -> Link
219−addQueryParam qp l =
220− l { _queryParams = _queryParams l <> [qp] }
221−
222−-- | Transform 'Link' into 'URI'.
223−--
224−-- >>> type API = "something" :> Get '[JSON] Int
225−-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)
226−-- something
227−--
228−-- >>> type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int
229−-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
230−-- sum?x[]=1&x[]=2&x[]=3
231−--
232−-- >>> type API = "foo/bar" :> Get '[JSON] Int
233−-- >>> linkURI $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API)
234−-- foo%2Fbar
235−--
236−-- >>> type SomeRoute = "abc" :> Capture "email" String :> Put '[JSON] ()
237−-- >>> let someRoute = Proxy :: Proxy SomeRoute
238−-- >>> safeLink someRoute someRoute "test@example.com"
239−-- Link {_segments = ["abc","test%40example.com"], _queryParams = []}
240−--
241−-- >>> linkURI $ safeLink someRoute someRoute "test@example.com"
242−-- abc/test%40example.com
243−--
244−linkURI :: Link -> URI
245−linkURI = linkURI' LinkArrayElementBracket
246−
247−-- | How to encode array query elements.
248−data LinkArrayElementStyle
249− = LinkArrayElementBracket -- ^ @foo[]=1&foo[]=2@
250− | LinkArrayElementPlain -- ^ @foo=1&foo=2@
251− deriving (Eq, Ord, Show, Enum, Bounded)
252−
253−-- | Configurable 'linkURI'.
254−--
255−-- >>> type API = "sum" :> QueryParams "x" Int :> Get '[JSON] Int
256−-- >>> linkURI' LinkArrayElementBracket $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
257−-- sum?x[]=1&x[]=2&x[]=3
258−--
259−-- >>> linkURI' LinkArrayElementPlain $ safeLink (Proxy :: Proxy API) (Proxy :: Proxy API) [1, 2, 3]
260−-- sum?x=1&x=2&x=3
261−--
262−linkURI' :: LinkArrayElementStyle -> Link -> URI
263−linkURI' addBrackets (Link segments q_params) =
264− URI mempty -- No scheme (relative)
265− Nothing -- Or authority (relative)
266− (intercalate "/" $ map getEscaped segments)
267− (makeQueries q_params) mempty
1+module Servant.Utils.Links
2+ {-# DEPRECATED "Use Servant.Links." #-}
3+ ( module Servant.Links )
2684 where
269− makeQueries :: [Param] -> String
270− makeQueries [] = ""
271− makeQueries xs =
272− "?" <> intercalate "&" (fmap makeQuery xs)
273−
274− makeQuery :: Param -> String
275− makeQuery (ArrayElemParam k v) = escape k <> style <> escape (Text.unpack v)
276− makeQuery (SingleParam k v) = escape k <> "=" <> escape (Text.unpack v)
277− makeQuery (FlagParam k) = escape k
278−
279− style = case addBrackets of
280− LinkArrayElementBracket -> "[]="
281− LinkArrayElementPlain -> "="
282−
283−escape :: String -> String
284−escape = escapeURIString isUnreserved
285−
286−-- | Create a valid (by construction) relative URI with query params.
287−--
288−-- This function will only typecheck if `endpoint` is part of the API `api`
289−safeLink
290− :: forall endpoint api. (IsElem endpoint api, HasLink endpoint)
291− => Proxy api -- ^ The whole API that this endpoint is a part of
292− -> Proxy endpoint -- ^ The API endpoint you would like to point to
293− -> MkLink endpoint Link
294−safeLink = safeLink' id
295−
296−-- | More general 'safeLink'.
297−--
298−safeLink'
299− :: forall endpoint api a. (IsElem endpoint api, HasLink endpoint)
300− => (Link -> a)
301− -> Proxy api -- ^ The whole API that this endpoint is a part of
302− -> Proxy endpoint -- ^ The API endpoint you would like to point to
303− -> MkLink endpoint a
304−safeLink' toA _ endpoint = toLink toA endpoint (Link mempty mempty)
305−
306−-- | Create all links in an API.
307−--
308−-- Note that the @api@ type must be restricted to the endpoints that have
309−-- valid links to them.
310−--
311−-- >>> type API = "foo" :> Capture "name" Text :> Get '[JSON] Text :<|> "bar" :> Capture "name" Int :> Get '[JSON] Double
312−-- >>> let fooLink :<|> barLink = allLinks (Proxy :: Proxy API)
313−-- >>> :t fooLink
314−-- fooLink :: Text -> Link
315−-- >>> :t barLink
316−-- barLink :: Int -> Link
317−--
318−-- Note: nested APIs don't work well with this approach
319−--
320−-- >>> :kind! MkLink (Capture "nest" Char :> (Capture "x" Int :> Get '[JSON] Int :<|> Capture "y" Double :> Get '[JSON] Double)) Link
321−-- MkLink (Capture "nest" Char :> (Capture "x" Int :> Get '[JSON] Int :<|> Capture "y" Double :> Get '[JSON] Double)) Link :: *
322−-- = Char -> (Int -> Link) :<|> (Double -> Link)
323−allLinks
324− :: forall api. HasLink api
325− => Proxy api
326− -> MkLink api Link
327−allLinks = allLinks' id
328−
329−-- | More general 'allLinks'. See `safeLink'`.
330−allLinks'
331− :: forall api a. HasLink api
332− => (Link -> a)
333− -> Proxy api
334− -> MkLink api a
335−allLinks' toA api = toLink toA api (Link mempty mempty)
336−
337−-- | Construct a toLink for an endpoint.
338−class HasLink endpoint where
339− type MkLink endpoint (a :: *)
340− toLink
341− :: (Link -> a)
342− -> Proxy endpoint -- ^ The API endpoint you would like to point to
343− -> Link
344− -> MkLink endpoint a
345−
346−-- Naked symbol instance
347−instance (KnownSymbol sym, HasLink sub) => HasLink (sym :> sub) where
348− type MkLink (sym :> sub) a = MkLink sub a
349− toLink toA _ =
350− toLink toA (Proxy :: Proxy sub) . addSegment (escaped seg)
351− where
352− seg = symbolVal (Proxy :: Proxy sym)
353−
354−-- QueryParam instances
355−instance (KnownSymbol sym, ToHttpApiData v, HasLink sub, SBoolI (FoldRequired mods))
356− => HasLink (QueryParam' mods sym v :> sub)
357− where
358− type MkLink (QueryParam' mods sym v :> sub) a = If (FoldRequired mods) v (Maybe v) -> MkLink sub a
359− toLink toA _ l mv =
360− toLink toA (Proxy :: Proxy sub) $
361− case sbool :: SBool (FoldRequired mods) of
362− STrue -> (addQueryParam . SingleParam k . toQueryParam) mv l
363− SFalse -> maybe id (addQueryParam . SingleParam k . toQueryParam) mv l
364− where
365− k :: String
366− k = symbolVal (Proxy :: Proxy sym)
367−
368−instance (KnownSymbol sym, ToHttpApiData v, HasLink sub)
369− => HasLink (QueryParams sym v :> sub)
370− where
371− type MkLink (QueryParams sym v :> sub) a = [v] -> MkLink sub a
372− toLink toA _ l =
373− toLink toA (Proxy :: Proxy sub) .
374− foldl' (\l' v -> addQueryParam (ArrayElemParam k (toQueryParam v)) l') l
375− where
376− k = symbolVal (Proxy :: Proxy sym)
377−
378−instance (KnownSymbol sym, HasLink sub)
379− => HasLink (QueryFlag sym :> sub)
380− where
381− type MkLink (QueryFlag sym :> sub) a = Bool -> MkLink sub a
382− toLink toA _ l False =
383− toLink toA (Proxy :: Proxy sub) l
384− toLink toA _ l True =
385− toLink toA (Proxy :: Proxy sub) $ addQueryParam (FlagParam k) l
386− where
387− k = symbolVal (Proxy :: Proxy sym)
388−
389−-- :<|> instance - Generate all links at once
390−instance (HasLink a, HasLink b) => HasLink (a :<|> b) where
391− type MkLink (a :<|> b) r = MkLink a r :<|> MkLink b r
392− toLink toA _ l = toLink toA (Proxy :: Proxy a) l :<|> toLink toA (Proxy :: Proxy b) l
393−
394−-- Misc instances
395−instance HasLink sub => HasLink (ReqBody' mods ct a :> sub) where
396− type MkLink (ReqBody' mods ct a :> sub) r = MkLink sub r
397− toLink toA _ = toLink toA (Proxy :: Proxy sub)
398−
399−instance (ToHttpApiData v, HasLink sub)
400− => HasLink (Capture' mods sym v :> sub)
401− where
402− type MkLink (Capture' mods sym v :> sub) a = v -> MkLink sub a
403− toLink toA _ l v =
404− toLink toA (Proxy :: Proxy sub) $
405− addSegment (escaped . Text.unpack $ toUrlPiece v) l
406−
407−instance (ToHttpApiData v, HasLink sub)
408− => HasLink (CaptureAll sym v :> sub)
409− where
410− type MkLink (CaptureAll sym v :> sub) a = [v] -> MkLink sub a
411− toLink toA _ l vs = toLink toA (Proxy :: Proxy sub) $
412− foldl' (flip $ addSegment . escaped . Text.unpack . toUrlPiece) l vs
413−
414−instance HasLink sub => HasLink (Header' mods sym (a :: *) :> sub) where
415− type MkLink (Header' mods sym a :> sub) r = MkLink sub r
416− toLink = simpleToLink (Proxy :: Proxy sub)
417−
418−instance HasLink sub => HasLink (Vault :> sub) where
419− type MkLink (Vault :> sub) a = MkLink sub a
420− toLink = simpleToLink (Proxy :: Proxy sub)
421−
422−instance HasLink sub => HasLink (Description s :> sub) where
423− type MkLink (Description s :> sub) a = MkLink sub a
424− toLink = simpleToLink (Proxy :: Proxy sub)
425−
426−instance HasLink sub => HasLink (Summary s :> sub) where
427− type MkLink (Summary s :> sub) a = MkLink sub a
428− toLink = simpleToLink (Proxy :: Proxy sub)
429−
430−instance HasLink sub => HasLink (HttpVersion :> sub) where
431− type MkLink (HttpVersion:> sub) a = MkLink sub a
432− toLink = simpleToLink (Proxy :: Proxy sub)
433−
434−instance HasLink sub => HasLink (IsSecure :> sub) where
435− type MkLink (IsSecure :> sub) a = MkLink sub a
436− toLink = simpleToLink (Proxy :: Proxy sub)
437−
438−instance HasLink sub => HasLink (WithNamedContext name context sub) where
439− type MkLink (WithNamedContext name context sub) a = MkLink sub a
440− toLink toA _ = toLink toA (Proxy :: Proxy sub)
441−
442−instance HasLink sub => HasLink (RemoteHost :> sub) where
443− type MkLink (RemoteHost :> sub) a = MkLink sub a
444− toLink = simpleToLink (Proxy :: Proxy sub)
445−
446−instance HasLink sub => HasLink (BasicAuth realm a :> sub) where
447− type MkLink (BasicAuth realm a :> sub) r = MkLink sub r
448− toLink = simpleToLink (Proxy :: Proxy sub)
449−
450−instance HasLink EmptyAPI where
451− type MkLink EmptyAPI a = EmptyAPI
452− toLink _ _ _ = EmptyAPI
453−
454−-- Verb (terminal) instances
455−instance HasLink (Verb m s ct a) where
456− type MkLink (Verb m s ct a) r = r
457− toLink toA _ = toA
458−
459−instance HasLink Raw where
460− type MkLink Raw a = a
461− toLink toA _ = toA
462−
463−instance HasLink (Stream m fr ct a) where
464− type MkLink (Stream m fr ct a) r = r
465− toLink toA _ = toA
466−
467−-- AuthProtext instances
468−instance HasLink sub => HasLink (AuthProtect tag :> sub) where
469− type MkLink (AuthProtect tag :> sub) a = MkLink sub a
470− toLink = simpleToLink (Proxy :: Proxy sub)
471−
472−-- | Helper for implemneting 'toLink' for combinators not affecting link
473−-- structure.
474−simpleToLink
475− :: forall sub a combinator.
476− (HasLink sub, MkLink sub a ~ MkLink (combinator :> sub) a)
477− => Proxy sub
478− -> (Link -> a)
479− -> Proxy (combinator :> sub)
480− -> Link
481− -> MkLink (combinator :> sub) a
482−simpleToLink _ toA _ = toLink toA (Proxy :: Proxy sub)
483−
4845
485−-- $setup
486−-- >>> import Servant.API
487−-- >>> import Data.Text (Text)
6+import Servant.Links
servant/test/Servant/Utils/LinksSpec.hs → servant/test/Servant/LinksSpec.hsrenamed+3−3
@@ -7,7 +7,7 @@
77#if __GLASGOW_HASKELL__ < 709
88{-# OPTIONS_GHC -fcontext-stack=41 #-}
99#endif
10−module Servant.Utils.LinksSpec where
10+module Servant.LinksSpec where
1111
1212import Data.Proxy (Proxy (..))
1313import Test.Hspec (Expectation, Spec, describe, it,
@@ -15,7 +15,7 @@ import Test.Hspec (Expectation, Spec, describe, it,
1515import Data.String (fromString)
1616
1717import Servant.API
18−import Servant.Utils.Links
18+import Servant.Links
1919import Servant.API.Internal.Test.ComprehensiveAPI (comprehensiveAPIWithoutRaw)
2020
2121type TestApi =
@@ -51,7 +51,7 @@ shouldBeLink link expected =
5151 toUrlPiece link `shouldBe` fromString expected
5252
5353spec :: Spec
54−spec = describe "Servant.Utils.Links" $ do
54+spec = describe "Servant.Links" $ do
5555 it "generates correct links for capture query params" $ do
5656 let l1 = Proxy :: Proxy ("hello" :> Capture "name" String :> Delete '[JSON] NoContent)
5757 apiLink l1 "hi" `shouldBeLink` "hello/hi"
setup.pynew file+11
@@ -0,0 +1,11 @@
1+from setuptools import setup
2+
3+setup(name='servant-rtd',
4+ version='0.0.1',
5+ description='Documentation for the haskell-servant platform',
6+ url='https://github.com/haskell-servant/servant',
7+ author='Servant Contributors',
8+ author_email='haskell-servant-maintainers@googlegroups.com',
9+ license='BSD3',
10+ zip_safe=False)
11+
stack.yaml+1−14
@@ -1,5 +1,5 @@
11# Let's try to keep resolver at the first day of the month
2−resolver: nightly-2018-03-01
2+resolver: nightly-2018-06-01
33packages:
44- servant-client/
55- servant-client-core/
@@ -8,18 +8,5 @@ packages:
88- servant-server/
99- servant/
1010
11−extra-deps:
12−- cabal-doctest-1.0.6
13−- http-api-data-0.3.7.2
14−- http-types-0.12
15−- text-1.2.3.0
16−- aeson-1.3.0.0
17−- exceptions-0.10.0
18−- aeson-compat-0.3.7.1
19−- free-5.0.1
20−- lens-4.16
21−- random-bytestring-0.1.3
22−- pcg-random-0.1.3.5
23−
2411# allow-newer: true # ignores all bounds, that's a sledgehammer
2512# - doc/tutorial/