git.iko.soy/typeable/openapi3
on master help sign in
.github/workflows/haskell-ci.ymlnew file+243
since splitsnapshots
unifiedsplit
@@ -0,0 +1,243 @@
1+# This GitHub workflow config has been generated by a script via
2+#
3+# haskell-ci 'github' 'cabal.project'
4+#
5+# To regenerate the script (for example after adjusting tested-with) run
6+#
7+# haskell-ci regenerate
8+#
9+# For more information, see https://github.com/haskell-CI/haskell-ci
10+#
11+# version: 0.14.3
12+#
13+# REGENDATA ("0.14.3",["github","cabal.project"])
14+#
15+name: Haskell-CI
16+on:
17+ push:
18+ branches:
19+ - master
20+ pull_request:
21+ branches:
22+ - master
23+jobs:
24+ linux:
25+ name: Haskell-CI - Linux - ${{ matrix.compiler }}
26+ runs-on: ubuntu-18.04
27+ timeout-minutes:
28+ 60
29+ container:
30+ image: buildpack-deps:focal
31+ continue-on-error: ${{ matrix.allow-failure }}
32+ strategy:
33+ matrix:
34+ include:
35+ - compiler: ghc-9.2.2
36+ compilerKind: ghc
37+ compilerVersion: 9.2.2
38+ setup-method: ghcup
39+ allow-failure: true
40+ - compiler: ghc-9.0.2
41+ compilerKind: ghc
42+ compilerVersion: 9.0.2
43+ setup-method: ghcup
44+ allow-failure: false
45+ - compiler: ghc-8.10.7
46+ compilerKind: ghc
47+ compilerVersion: 8.10.7
48+ setup-method: ghcup
49+ allow-failure: false
50+ - compiler: ghc-8.8.4
51+ compilerKind: ghc
52+ compilerVersion: 8.8.4
53+ setup-method: hvr-ppa
54+ allow-failure: false
55+ - compiler: ghc-8.6.5
56+ compilerKind: ghc
57+ compilerVersion: 8.6.5
58+ setup-method: hvr-ppa
59+ allow-failure: false
60+ - compiler: ghc-8.4.4
61+ compilerKind: ghc
62+ compilerVersion: 8.4.4
63+ setup-method: hvr-ppa
64+ allow-failure: false
65+ fail-fast: false
66+ steps:
67+ - name: apt
68+ run: |
69+ apt-get update
70+ apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
71+ if [ "${{ matrix.setup-method }}" = ghcup ]; then
72+ mkdir -p "$HOME/.ghcup/bin"
73+ curl -sL https://downloads.haskell.org/ghcup/0.1.17.5/x86_64-linux-ghcup-0.1.17.5 > "$HOME/.ghcup/bin/ghcup"
74+ chmod a+x "$HOME/.ghcup/bin/ghcup"
75+ "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER"
76+ "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0
77+ else
78+ apt-add-repository -y 'ppa:hvr/ghc'
79+ apt-get update
80+ apt-get install -y "$HCNAME"
81+ mkdir -p "$HOME/.ghcup/bin"
82+ curl -sL https://downloads.haskell.org/ghcup/0.1.17.5/x86_64-linux-ghcup-0.1.17.5 > "$HOME/.ghcup/bin/ghcup"
83+ chmod a+x "$HOME/.ghcup/bin/ghcup"
84+ "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0
85+ fi
86+ env:
87+ HCKIND: ${{ matrix.compilerKind }}
88+ HCNAME: ${{ matrix.compiler }}
89+ HCVER: ${{ matrix.compilerVersion }}
90+ - name: Set PATH and environment variables
91+ run: |
92+ echo "$HOME/.cabal/bin" >> $GITHUB_PATH
93+ echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
94+ echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
95+ echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
96+ HCDIR=/opt/$HCKIND/$HCVER
97+ if [ "${{ matrix.setup-method }}" = ghcup ]; then
98+ HC=$HOME/.ghcup/bin/$HCKIND-$HCVER
99+ echo "HC=$HC" >> "$GITHUB_ENV"
100+ echo "HCPKG=$HOME/.ghcup/bin/$HCKIND-pkg-$HCVER" >> "$GITHUB_ENV"
101+ echo "HADDOCK=$HOME/.ghcup/bin/haddock-$HCVER" >> "$GITHUB_ENV"
102+ echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
103+ else
104+ HC=$HCDIR/bin/$HCKIND
105+ echo "HC=$HC" >> "$GITHUB_ENV"
106+ echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
107+ echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
108+ echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
109+ fi
110+
111+ HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
112+ echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
113+ echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
114+ echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
115+ echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
116+ echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
117+ echo "GHCJSARITH=0" >> "$GITHUB_ENV"
118+ env:
119+ HCKIND: ${{ matrix.compilerKind }}
120+ HCNAME: ${{ matrix.compiler }}
121+ HCVER: ${{ matrix.compilerVersion }}
122+ - name: env
123+ run: |
124+ env
125+ - name: write cabal config
126+ run: |
127+ mkdir -p $CABAL_DIR
128+ cat >> $CABAL_CONFIG <<EOF
129+ remote-build-reporting: anonymous
130+ write-ghc-environment-files: never
131+ remote-repo-cache: $CABAL_DIR/packages
132+ logs-dir: $CABAL_DIR/logs
133+ world-file: $CABAL_DIR/world
134+ extra-prog-path: $CABAL_DIR/bin
135+ symlink-bindir: $CABAL_DIR/bin
136+ installdir: $CABAL_DIR/bin
137+ build-summary: $CABAL_DIR/logs/build.log
138+ store-dir: $CABAL_DIR/store
139+ install-dirs user
140+ prefix: $CABAL_DIR
141+ repository hackage.haskell.org
142+ url: http://hackage.haskell.org/
143+ EOF
144+ cat >> $CABAL_CONFIG <<EOF
145+ program-default-options
146+ ghc-options: $GHCJOBS +RTS -M3G -RTS
147+ EOF
148+ cat $CABAL_CONFIG
149+ - name: versions
150+ run: |
151+ $HC --version || true
152+ $HC --print-project-git-commit-id || true
153+ $CABAL --version || true
154+ - name: update cabal index
155+ run: |
156+ $CABAL v2-update -v
157+ - name: install cabal-plan
158+ run: |
159+ mkdir -p $HOME/.cabal/bin
160+ curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.6.2.0/cabal-plan-0.6.2.0-x86_64-linux.xz > cabal-plan.xz
161+ echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
162+ xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
163+ rm -f cabal-plan.xz
164+ chmod a+x $HOME/.cabal/bin/cabal-plan
165+ cabal-plan --version
166+ - name: checkout
167+ uses: actions/checkout@v2
168+ with:
169+ path: source
170+ - name: initial cabal.project for sdist
171+ run: |
172+ touch cabal.project
173+ echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project
174+ cat cabal.project
175+ - name: sdist
176+ run: |
177+ mkdir -p sdist
178+ $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
179+ - name: unpack
180+ run: |
181+ mkdir -p unpacked
182+ find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
183+ - name: generate cabal.project
184+ run: |
185+ PKGDIR_openapi3="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/openapi3-[0-9.]*')"
186+ echo "PKGDIR_openapi3=${PKGDIR_openapi3}" >> "$GITHUB_ENV"
187+ rm -f cabal.project cabal.project.local
188+ touch cabal.project
189+ touch cabal.project.local
190+ echo "packages: ${PKGDIR_openapi3}" >> cabal.project
191+ echo "package openapi3" >> cabal.project
192+ echo " ghc-options: -Werror=missing-methods" >> cabal.project
193+ cat >> cabal.project <<EOF
194+ allow-newer: http-media:base
195+ EOF
196+ $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(openapi3)$/; }' >> cabal.project.local
197+ cat cabal.project
198+ cat cabal.project.local
199+ - name: dump install plan
200+ run: |
201+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
202+ cabal-plan
203+ - name: cache
204+ uses: actions/cache@v2
205+ with:
206+ key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
207+ path: ~/.cabal/store
208+ restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
209+ - name: install dependencies
210+ run: |
211+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
212+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
213+ - name: build w/o tests
214+ run: |
215+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
216+ - name: build
217+ run: |
218+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
219+ - name: tests
220+ run: |
221+ $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
222+ - name: cabal check
223+ run: |
224+ cd ${PKGDIR_openapi3} || false
225+ ${CABAL} -vnormal check
226+ - name: haddock
227+ run: |
228+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
229+ - name: unconstrained build
230+ run: |
231+ rm -f cabal.project.local
232+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
233+ - name: prepare for constraint sets
234+ run: |
235+ rm -f cabal.project.local
236+ - name: constraint set aeson-2
237+ run: |
238+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson >=2.0' --dependencies-only -j2 all
239+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson >=2.0' all
240+ - name: constraint set aeson-1
241+ run: |
242+ if [ $((HCNUMVER < 90200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson <2.0' --dependencies-only -j2 all ; fi
243+ if [ $((HCNUMVER < 90200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson <2.0' all ; fi
.travis.ymldeleted−155
@@ -1,155 +0,0 @@
1−# This Travis job script has been generated by a script via
2−#
3−# haskell-ci '--branches' 'master' '--haddock-jobs=>=8.4' '--output' '.travis.yml' 'swagger2.cabal'
4−#
5−# To regenerate the script (for example after adjusting tested-with) run
6−#
7−# haskell-ci regenerate
8−#
9−# For more information, see https://github.com/haskell-CI/haskell-ci
10−#
11−# version: 0.10
12−#
13−version: ~> 1.0
14−language: c
15−os: linux
16−dist: xenial
17−git:
18− # whether to recursively clone submodules
19− submodules: false
20−branches:
21− only:
22− - master
23−cache:
24− directories:
25− - $HOME/.cabal/packages
26− - $HOME/.cabal/store
27− - $HOME/.hlint
28−before_cache:
29− - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
30− # remove files that are regenerated by 'cabal update'
31− - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
32− - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
33− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
34− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
35− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
36− - rm -rfv $CABALHOME/packages/head.hackage
37−jobs:
38− include:
39− - compiler: ghc-8.10.1
40− addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.10.1","cabal-install-3.2"]}}
41− os: linux
42− - compiler: ghc-8.8.1
43− addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.8.1","cabal-install-3.2"]}}
44− os: linux
45− - compiler: ghc-8.6.5
46− addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.6.5","cabal-install-3.2"]}}
47− os: linux
48− - compiler: ghc-8.4.4
49− addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.4.4","cabal-install-3.2"]}}
50− os: linux
51− - compiler: ghc-8.2.2
52− addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.2.2","cabal-install-3.2"]}}
53− os: linux
54− - compiler: ghc-8.0.2
55− addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.0.2","cabal-install-3.2"]}}
56− os: linux
57−before_install:
58− - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
59− - WITHCOMPILER="-w $HC"
60− - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
61− - HCPKG="$HC-pkg"
62− - unset CC
63− - CABAL=/opt/ghc/bin/cabal
64− - CABALHOME=$HOME/.cabal
65− - export PATH="$CABALHOME/bin:$PATH"
66− - TOP=$(pwd)
67− - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
68− - echo $HCNUMVER
69− - CABAL="$CABAL -vnormal+nowrap"
70− - set -o pipefail
71− - TEST=--enable-tests
72− - BENCH=--enable-benchmarks
73− - HEADHACKAGE=false
74− - rm -f $CABALHOME/config
75− - |
76− echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
77− echo "remote-build-reporting: anonymous" >> $CABALHOME/config
78− echo "write-ghc-environment-files: always" >> $CABALHOME/config
79− echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
80− echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
81− echo "world-file: $CABALHOME/world" >> $CABALHOME/config
82− echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
83− echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
84− echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
85− echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
86− echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
87− echo "install-dirs user" >> $CABALHOME/config
88− echo " prefix: $CABALHOME" >> $CABALHOME/config
89− echo "repository hackage.haskell.org" >> $CABALHOME/config
90− echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
91−install:
92− - ${CABAL} --version
93− - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
94− - |
95− echo "program-default-options" >> $CABALHOME/config
96− echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config
97− - cat $CABALHOME/config
98− - rm -fv cabal.project cabal.project.local cabal.project.freeze
99− - travis_retry ${CABAL} v2-update -v
100− # Generate cabal.project
101− - rm -rf cabal.project cabal.project.local cabal.project.freeze
102− - touch cabal.project
103− - |
104− echo "packages: ." >> cabal.project
105− - if [ $HCNUMVER -ge 80200 ] ; then echo 'package swagger2' >> cabal.project ; fi
106− - "if [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
107− - |
108− - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(swagger2)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
109− - cat cabal.project || true
110− - cat cabal.project.local || true
111− - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
112− - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH}
113− - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
114− - rm cabal.project.freeze
115− - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all
116− - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all
117−script:
118− - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
119− # Packaging...
120− - ${CABAL} v2-sdist all
121− # Unpacking...
122− - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
123− - cd ${DISTDIR} || false
124− - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
125− - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
126− - PKGDIR_swagger2="$(find . -maxdepth 1 -type d -regex '.*/swagger2-[0-9.]*')"
127− # Generate cabal.project
128− - rm -rf cabal.project cabal.project.local cabal.project.freeze
129− - touch cabal.project
130− - |
131− echo "packages: ${PKGDIR_swagger2}" >> cabal.project
132− - if [ $HCNUMVER -ge 80200 ] ; then echo 'package swagger2' >> cabal.project ; fi
133− - "if [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
134− - |
135− - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(swagger2)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
136− - cat cabal.project || true
137− - cat cabal.project.local || true
138− # Building...
139− # this builds all libraries and executables (without tests/benchmarks)
140− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all
141− # Building with tests and benchmarks...
142− # build & run tests, build benchmarks
143− - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all
144− # Testing...
145− - ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all
146− # cabal check...
147− - (cd ${PKGDIR_swagger2} && ${CABAL} -vnormal check)
148− # haddock...
149− - if [ $HCNUMVER -ge 80400 ] ; then ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all ; fi
150− # Building without installed constraints for packages in global-db...
151− - rm -f cabal.project.local
152− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all
153−
154−# REGENDATA ("0.10",["--branches","master","--haddock-jobs=>=8.4","--output",".travis.yml","swagger2.cabal"])
155−# EOF
CHANGELOG.md+47
@@ -1,3 +1,50 @@
1+Unreleased
2+----------
3+
4+3.2.2
5+-----
6+
7+- Fix bug with infinite recursion in GToSchema [#37](https://github.com/biocad/openapi3/pull/37).
8+
9+3.2.1
10+-----
11+
12+- Support aeson-2.0.3.
13+- Support GHC-9.2 (doctests still fail).
14+
15+3.2.0
16+-----
17+
18+- Support aeson-2 [#34](https://github.com/biocad/openapi3/pull/34).
19+- Use `SecurityDefinitions` type for `_componentsSecuritySchemes`
20+ [#32](https://github.com/biocad/openapi3/pull/32).
21+
22+3.1.0
23+-----
24+
25+- Use `format: date-time` for `ToParamSchema ZonedTime` instance (see
26+ [#20](https://github.com/biocad/openapi3/pull/20)).
27+- Support generating schema for any polymorphic types via `Typeable` (see
28+ [#19](https://github.com/biocad/openapi3/pull/19)).
29+- Allow `hashable-1.3.1`, prettify doctests (see [#18](https://github.com/biocad/openapi3/pull/18)).
30+
31+3.0.2.0
32+-------
33+
34+- Fix definition for HTTP security scheme (see [#10](https://github.com/biocad/openapi3/pull/10));
35+- Fix error message in `exclusiveMaximum` / `exclusiveMinimum` validation (see
36+ [#15](https://github.com/biocad/openapi3/pull/15));
37+
38+3.0.1.0
39+-------
40+
41+- Support polymorphic types in schema generator.
42+
43+3.0.0
44+-----
45+
46+- Switch to OpenAPI 3.0 specification.
47+
12.6.1
482.6.1
2-----
49-----
3
50
README.md+13−11
@@ -1,21 +1,22 @@
1−# swagger2
1+# OpenApi 3
2
2
3−[![Join the chat at https://gitter.im/haskell-swagger2/Lobby](https://badges.gitter.im/haskell-swagger2/Lobby.svg)](https://gitter.im/haskell-swagger2/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3+[![Hackage](https://img.shields.io/hackage/v/openapi3.svg)](http://hackage.haskell.org/package/openapi3)
4+[![Build Status](https://travis-ci.org/biocad/openapi3.svg?branch=master)](https://travis-ci.org/biocad/openapi3)
5+[![Stackage LTS](http://stackage.org/package/openapi3/badge/lts)](http://stackage.org/lts/package/opeopenapi3)
6+[![Stackage Nightly](http://stackage.org/package/openapi3/badge/nightly)](http://stackage.org/nightly/package/openapi3)
4
7
5−[![Hackage](https://img.shields.io/hackage/v/swagger2.svg)](http://hackage.haskell.org/package/swagger2)
8+OpenAPI 3.0 data model.
6−[![Build Status](https://travis-ci.org/GetShopTV/swagger2.svg?branch=master)](https://travis-ci.org/GetShopTV/swagger2)
7−[![Stackage LTS](http://stackage.org/package/swagger2/badge/lts)](http://stackage.org/lts/package/swagger2)
8−[![Stackage Nightly](http://stackage.org/package/swagger2/badge/nightly)](http://stackage.org/nightly/package/swagger2)
9
9
10−Swagger 2.0 data model.
10+The original OpenAPI 3.0 specification is available at http://swagger.io/specification/.
11
11
12−The original Swagger 2.0 specification is available at http://swagger.io/specification/.
12+This package is heavily based on excellent work on Swagger 2.0 at
13+https://github.com/GetShopTV/swagger2.
13
14
14## Usage
15## Usage
15
16
16−This library is intended to be used for decoding and encoding Swagger 2.0 API specifications as well as manipulating them.
17+This library is intended to be used for decoding and encoding OpenApi 3.0.3 specifications as well as manipulating them.
17
18
18−Please refer to [haddock documentation](http://hackage.haskell.org/package/swagger2).
19+Please refer to [haddock documentation](http://hackage.haskell.org/package/openapi3).
19
20
20Some examples can be found in [`examples/` directory](/examples).
21Some examples can be found in [`examples/` directory](/examples).
21
22
@@ -33,7 +34,8 @@ Many Swagger tools, including server and client code generation for many languag
33
34
34We are happy to receive bug reports, fixes, documentation enhancements, and other improvements.
35We are happy to receive bug reports, fixes, documentation enhancements, and other improvements.
35
36
36−Please report bugs via the [github issue tracker](https://github.com/GetShopTV/swagger2/issues).
37+Please report bugs via the [github issue tracker](https://github.com/biocad/openapi3/issues).
37
38
38*GetShopTV Team*
39*GetShopTV Team*
39
40
41+*Biocad Team*
cabal.haskell-cinew file+11
@@ -0,0 +1,11 @@
1+branches: master
2+
3+distribution: focal
4+allow-failures: >=9.2
5+
6+constraint-set aeson-1
7+ constraints: aeson <2.0
8+ ghc: <9.2
9+
10+constraint-set aeson-2
11+ constraints: aeson >=2.0
cabal.project+2
@@ -1,2 +1,4 @@
1packages: .
1packages: .
2tests: true
2tests: true
3+
4+allow-newer: http-media:base
examples/hackage.hs+16−20
@@ -10,10 +10,10 @@ import Data.Proxy
10import Data.Text (Text)
10import Data.Text (Text)
11import GHC.Generics
11import GHC.Generics
12
12
13−import Data.Swagger
13+import Data.OpenApi
14−import Data.Swagger.Declare
14+import Data.OpenApi.Declare
15−import Data.Swagger.Lens
15+import Data.OpenApi.Lens
16−import Data.Swagger.Operation
16+import Data.OpenApi.Operation
17
17
18type Username = Text
18type Username = Text
19
19
@@ -27,7 +27,7 @@ instance ToSchema UserSummary where
27 usernameSchema <- declareSchemaRef (Proxy :: Proxy Username)
27 usernameSchema <- declareSchemaRef (Proxy :: Proxy Username)
28 useridSchema <- declareSchemaRef (Proxy :: Proxy Int)
28 useridSchema <- declareSchemaRef (Proxy :: Proxy Int)
29 return $ NamedSchema (Just "UserSummary") $ mempty
29 return $ NamedSchema (Just "UserSummary") $ mempty
30− & type_ ?~ SwaggerObject
30+ & type_ ?~ OpenApiObject
31 & properties .~
31 & properties .~
32 [ ("summaryUsername", usernameSchema )
32 [ ("summaryUsername", usernameSchema )
33 , ("summaryUserid" , useridSchema )
33 , ("summaryUserid" , useridSchema )
@@ -47,40 +47,36 @@ data UserDetailed = UserDetailed
47newtype Package = Package { packageName :: Text }
47newtype Package = Package { packageName :: Text }
48 deriving (Generic, ToSchema)
48 deriving (Generic, ToSchema)
49
49
50−hackageSwagger :: Swagger
50+hackageOpenApi :: OpenApi
51−hackageSwagger = spec & definitions .~ defs
51+hackageOpenApi = spec & components.schemas .~ defs
52 where
52 where
53− (defs, spec) = runDeclare declareHackageSwagger mempty
53+ (defs, spec) = runDeclare declareHackageOpenApi mempty
54
54
55−declareHackageSwagger :: Declare (Definitions Schema) Swagger
55+declareHackageOpenApi :: Declare (Definitions Schema) OpenApi
56−declareHackageSwagger = do
56+declareHackageOpenApi = do
57 -- param schemas
57 -- param schemas
58 let usernameParamSchema = toParamSchema (Proxy :: Proxy Username)
58 let usernameParamSchema = toParamSchema (Proxy :: Proxy Username)
59
59
60 -- responses
60 -- responses
61− userSummaryResponse <- declareResponse (Proxy :: Proxy UserSummary)
61+ userSummaryResponse <- declareResponse "application/json" (Proxy :: Proxy UserSummary)
62− userDetailedResponse <- declareResponse (Proxy :: Proxy UserDetailed)
62+ userDetailedResponse <- declareResponse "application/json" (Proxy :: Proxy UserDetailed)
63− packagesResponse <- declareResponse (Proxy :: Proxy [Package])
63+ packagesResponse <- declareResponse "application/json" (Proxy :: Proxy [Package])
64
64
65 return $ mempty
65 return $ mempty
66 & paths .~
66 & paths .~
67 [ ("/users", mempty & get ?~ (mempty
67 [ ("/users", mempty & get ?~ (mempty
68− & produces ?~ MimeList ["application/json"]
69 & at 200 ?~ Inline userSummaryResponse))
68 & at 200 ?~ Inline userSummaryResponse))
70 , ("/user/{username}", mempty & get ?~ (mempty
69 , ("/user/{username}", mempty & get ?~ (mempty
71− & produces ?~ MimeList ["application/json"]
72 & parameters .~ [ Inline $ mempty
70 & parameters .~ [ Inline $ mempty
73 & name .~ "username"
71 & name .~ "username"
74 & required ?~ True
72 & required ?~ True
75− & schema .~ ParamOther (mempty
76− & in_ .~ ParamPath
73+ & in_ .~ ParamPath
77− & paramSchema .~ usernameParamSchema) ]
74+ & schema ?~ Inline usernameParamSchema ]
78 & at 200 ?~ Inline userDetailedResponse))
75 & at 200 ?~ Inline userDetailedResponse))
79 , ("/packages", mempty & get ?~ (mempty
76 , ("/packages", mempty & get ?~ (mempty
80− & produces ?~ MimeList ["application/json"]
81 & at 200 ?~ Inline packagesResponse))
77 & at 200 ?~ Inline packagesResponse))
82 ]
78 ]
83
79
84main :: IO ()
80main :: IO ()
85−main = putStrLn . read . show . encode $ hackageSwagger
81+main = putStrLn . read . show . encode $ hackageOpenApi
86
82
swagger2.cabal → openapi3.cabalrenamed+65−57
@@ -1,34 +1,34 @@
1cabal-version: >=1.10
1cabal-version: >=1.10
2−name: swagger2
2+name: openapi3
3−version: 2.6.1
3+version: 3.2.2
4
4
5−synopsis: Swagger 2.0 data model
5+synopsis: OpenAPI 3.0 data model
6−category: Web, Swagger
6+category: Web, Swagger, OpenApi
7description:
7description:
8− This library is intended to be used for decoding and encoding Swagger 2.0 API
8+ This library is intended to be used for decoding and encoding OpenAPI 3.0 API
9 specifications as well as manipulating them.
9 specifications as well as manipulating them.
10 .
10 .
11− The original Swagger 2.0 specification is available at http://swagger.io/specification/.
11+ The original OpenAPI 3.0 specification is available at http://swagger.io/specification/.
12
12
13−homepage: https://github.com/GetShopTV/swagger2
13+homepage: https://github.com/biocad/openapi3
14−bug-reports: https://github.com/GetShopTV/swagger2/issues
14+bug-reports: https://github.com/biocad/openapi3/issues
15license: BSD3
15license: BSD3
16license-file: LICENSE
16license-file: LICENSE
17−author: Nickolay Kudasov
17+author: Nickolay Kudasov, Maxim Koltsov
18−maintainer: nickolay@getshoptv.com
18+maintainer: nickolay@getshoptv.com, kolmax94@gmail.com
19−copyright: (c) 2015-2016, GetShopTV
19+copyright: (c) 2015-2016, GetShopTV, (c) 2020, Biocad
20build-type: Custom
20build-type: Custom
21extra-source-files:
21extra-source-files:
22 README.md
22 README.md
23 , CHANGELOG.md
23 , CHANGELOG.md
24 , examples/*.hs
24 , examples/*.hs
25tested-with:
25tested-with:
26− GHC ==8.0.2
26+ GHC ==8.4.4
27− || ==8.2.2
28− || ==8.4.4
29 || ==8.6.5
27 || ==8.6.5
30− || ==8.8.1
28+ || ==8.8.4
31− || ==8.10.1
29+ || ==8.10.7
30+ || ==9.0.2
31+ || ==9.2.2
32
32
33custom-setup
33custom-setup
34 setup-depends:
34 setup-depends:
@@ -37,54 +37,56 @@ custom-setup
37library
37library
38 hs-source-dirs: src
38 hs-source-dirs: src
39 exposed-modules:
39 exposed-modules:
40− Data.Swagger
40+ Data.OpenApi
41− Data.Swagger.Declare
41+ Data.OpenApi.Declare
42− Data.Swagger.Lens
42+ Data.OpenApi.Lens
43− Data.Swagger.Operation
43+ Data.OpenApi.Operation
44− Data.Swagger.Optics
44+ Data.OpenApi.Optics
45− Data.Swagger.ParamSchema
45+ Data.OpenApi.ParamSchema
46− Data.Swagger.Schema
46+ Data.OpenApi.Schema
47− Data.Swagger.Schema.Generator
47+ Data.OpenApi.Schema.Generator
48− Data.Swagger.Schema.Validation
48+ Data.OpenApi.Schema.Validation
49− Data.Swagger.SchemaOptions
49+ Data.OpenApi.SchemaOptions
50
50
51 -- internal modules
51 -- internal modules
52− Data.Swagger.Internal
52+ Data.OpenApi.Internal
53− Data.Swagger.Internal.Schema
53+ Data.OpenApi.Internal.Schema
54− Data.Swagger.Internal.Schema.Validation
54+ Data.OpenApi.Internal.Schema.Validation
55− Data.Swagger.Internal.ParamSchema
55+ Data.OpenApi.Internal.ParamSchema
56− Data.Swagger.Internal.Utils
56+ Data.OpenApi.Internal.Utils
57− Data.Swagger.Internal.AesonUtils
57+ Data.OpenApi.Internal.AesonUtils
58− Data.Swagger.Internal.TypeShape
58+ Data.OpenApi.Internal.TypeShape
59+
60+ Data.OpenApi.Aeson.Compat
59
61
60 -- GHC boot libraries
62 -- GHC boot libraries
61 build-depends:
63 build-depends:
62− base >=4.9 && <4.15
64+ base >=4.11.1.0 && <4.17
63− , bytestring >=0.10.8.1 && <0.11
65+ , bytestring >=0.10.8.2 && <0.12
64− , containers >=0.5.7.1 && <0.7
66+ , containers >=0.5.11.0 && <0.7
65− , template-haskell >=2.11.1.0 && <2.17
67+ , template-haskell >=2.13.0.0 && <2.19
66− , time >=1.6.0.1 && <1.10
68+ , time >=1.8.0.2 && <1.14
67− , transformers >=0.5.2.0 && <0.6
69+ , transformers >=0.5.5.0 && <0.6
68
70
69 build-depends:
71 build-depends:
70 mtl >=2.2.2 && <2.3
72 mtl >=2.2.2 && <2.3
71− , text >=1.2.3.0 && <1.3
73+ , text >=1.2.3.1 && <2.1
72
74
73 -- other dependencies
75 -- other dependencies
74 build-depends:
76 build-depends:
75− base-compat-batteries >=0.11.1 && <0.12
77+ base-compat-batteries >=0.11.1 && <0.13
76− , aeson >=1.4.2.0 && <1.6
78+ , aeson >=1.4.2.0 && <1.6 || >=2.0.1.0 && < 2.1
77 , aeson-pretty >=0.8.7 && <0.9
79 , aeson-pretty >=0.8.7 && <0.9
78 -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint
80 -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint
79 , cookie >=0.4.3 && <0.5
81 , cookie >=0.4.3 && <0.5
80 , generics-sop >=0.5.1.0 && <0.6
82 , generics-sop >=0.5.1.0 && <0.6
81− , hashable >=1.2.7.0 && <1.4
83+ , hashable >=1.2.7.0 && <1.5
82 , http-media >=0.8.0.0 && <0.9
84 , http-media >=0.8.0.0 && <0.9
83 , insert-ordered-containers >=0.2.3 && <0.3
85 , insert-ordered-containers >=0.2.3 && <0.3
84− , lens >=4.16.1 && <4.20
86+ , lens >=4.16.1 && <5.2
85 , network >=2.6.3.5 && <3.2
87 , network >=2.6.3.5 && <3.2
86− , optics-core >=0.2 && <0.4
88+ , optics-core >=0.2 && <0.5
87− , optics-th >=0.2 && <0.4
89+ , optics-th >=0.2 && <0.5
88 , scientific >=0.3.6.2 && <0.4
90 , scientific >=0.3.6.2 && <0.4
89 , unordered-containers >=0.2.9.0 && <0.3
91 , unordered-containers >=0.2.9.0 && <0.3
90 , uuid-types >=1.0.3 && <1.1
92 , uuid-types >=1.0.3 && <1.1
@@ -102,7 +104,7 @@ test-suite spec
102 -- We need aeson's toEncoding for doctests too
104 -- We need aeson's toEncoding for doctests too
103 build-depends:
105 build-depends:
104 base
106 base
105− , swagger2
107+ , QuickCheck
106 , aeson
108 , aeson
107 , base-compat-batteries
109 , base-compat-batteries
108 , bytestring
110 , bytestring
@@ -111,32 +113,32 @@ test-suite spec
111 , insert-ordered-containers
113 , insert-ordered-containers
112 , lens
114 , lens
113 , mtl
115 , mtl
116+ , openapi3
114 , template-haskell
117 , template-haskell
115 , text
118 , text
116 , time
119 , time
117 , unordered-containers
120 , unordered-containers
118 , vector
121 , vector
119− , QuickCheck
120
122
121 -- test-suite only dependencies
123 -- test-suite only dependencies
122 build-depends:
124 build-depends:
123− hspec >=2.5.5 && <2.8
125+ hspec >=2.5.5 && <2.10
124 , HUnit >=1.6.0.0 && <1.7
126 , HUnit >=1.6.0.0 && <1.7
125 , quickcheck-instances >=0.3.19 && <0.14
127 , quickcheck-instances >=0.3.19 && <0.14
126 , utf8-string >=1.0.1.1 && <1.1
128 , utf8-string >=1.0.1.1 && <1.1
127
129
128 -- https://github.com/haskell/cabal/issues/3708
130 -- https://github.com/haskell/cabal/issues/3708
129 build-tool-depends:
131 build-tool-depends:
130− hspec-discover:hspec-discover >=2.5.5 && <2.8
132+ hspec-discover:hspec-discover >=2.5.5 && <2.10
131
133
132 other-modules:
134 other-modules:
133 SpecCommon
135 SpecCommon
134− Data.SwaggerSpec
136+ Data.OpenApiSpec
135− Data.Swagger.CommonTestTypes
137+ Data.OpenApi.CommonTestTypes
136− Data.Swagger.ParamSchemaSpec
138+ Data.OpenApi.ParamSchemaSpec
137− Data.Swagger.SchemaSpec
139+ Data.OpenApi.SchemaSpec
138− Data.Swagger.Schema.ValidationSpec
140+ Data.OpenApi.Schema.ValidationSpec
139− Data.Swagger.Schema.GeneratorSpec
141+ Data.OpenApi.Schema.GeneratorSpec
140 default-language: Haskell2010
142 default-language: Haskell2010
141
143
142test-suite doctests
144test-suite doctests
@@ -147,6 +149,12 @@ test-suite doctests
147 main-is: doctests.hs
149 main-is: doctests.hs
148 type: exitcode-stdio-1.0
150 type: exitcode-stdio-1.0
149
151
152+executable example
153+ hs-source-dirs: examples
154+ main-is: hackage.hs
155+ default-language: Haskell2010
156+ build-depends: base, aeson, lens, openapi3, text
157+
150source-repository head
158source-repository head
151 type: git
159 type: git
152− location: https://github.com/GetShopTV/swagger2.git
160+ location: https://github.com/biocad/openapi3.git
src/Data/Swagger.hs → src/Data/OpenApi.hsrenamed+184−91
@@ -1,5 +1,5 @@
1-- |
1-- |
2−-- Module: Data.Swagger
2+-- Module: Data.OpenApi
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
4-- Stability: experimental
4-- Stability: experimental
5--
5--
@@ -9,7 +9,7 @@
9-- These files can then be used by the Swagger-UI project to display the API
9-- These files can then be used by the Swagger-UI project to display the API
10-- and Swagger-Codegen to generate clients in various languages.
10-- and Swagger-Codegen to generate clients in various languages.
11-- Additional utilities can also take advantage of the resulting files, such as testing tools.
11-- Additional utilities can also take advantage of the resulting files, such as testing tools.
12−module Data.Swagger (
12+module Data.OpenApi (
13 -- * How to use this library
13 -- * How to use this library
14 -- $howto
14 -- $howto
15
15
@@ -29,15 +29,15 @@ module Data.Swagger (
29 -- $validation
29 -- $validation
30
30
31 -- * Re-exports
31 -- * Re-exports
32− module Data.Swagger.Lens,
32+ module Data.OpenApi.Lens,
33− module Data.Swagger.Optics,
33+ module Data.OpenApi.Optics,
34− module Data.Swagger.Operation,
34+ module Data.OpenApi.Operation,
35− module Data.Swagger.ParamSchema,
35+ module Data.OpenApi.ParamSchema,
36− module Data.Swagger.Schema,
36+ module Data.OpenApi.Schema,
37− module Data.Swagger.Schema.Validation,
37+ module Data.OpenApi.Schema.Validation,
38
38
39 -- * Swagger specification
39 -- * Swagger specification
40− Swagger(..),
40+ OpenApi(..),
41 Server(..),
41 Server(..),
42 ServerVariable(..),
42 ServerVariable(..),
43 Components(..),
43 Components(..),
@@ -56,10 +56,10 @@ module Data.Swagger (
56 TagName,
56 TagName,
57
57
58 -- ** Types and formats
58 -- ** Types and formats
59− SwaggerType(..),
59+ OpenApiType(..),
60 Format,
60 Format,
61 Definitions,
61 Definitions,
62− CollectionFormat(..),
62+ Style(..),
63
63
64 -- ** Parameters
64 -- ** Parameters
65 Param(..),
65 Param(..),
@@ -73,10 +73,9 @@ module Data.Swagger (
73 Encoding(..),
73 Encoding(..),
74
74
75 -- ** Schemas
75 -- ** Schemas
76− ParamSchema(..),
77 Schema(..),
76 Schema(..),
78 NamedSchema(..),
77 NamedSchema(..),
79− SwaggerItems(..),
78+ OpenApiItems(..),
80 Xml(..),
79 Xml(..),
81 Pattern,
80 Pattern,
82 AdditionalProperties(..),
81 AdditionalProperties(..),
@@ -86,10 +85,14 @@ module Data.Swagger (
86 Responses(..),
85 Responses(..),
87 Response(..),
86 Response(..),
88 HttpStatusCode,
87 HttpStatusCode,
88+ Link(..),
89+ Callback(..),
89
90
90 -- ** Security
91 -- ** Security
91 SecurityScheme(..),
92 SecurityScheme(..),
92 SecuritySchemeType(..),
93 SecuritySchemeType(..),
94+ HttpSchemeType(..),
95+ SecurityDefinitions(..),
93 SecurityRequirement(..),
96 SecurityRequirement(..),
94
97
95 -- *** API key
98 -- *** API key
@@ -118,14 +121,14 @@ module Data.Swagger (
118 URL(..),
121 URL(..),
119) where
122) where
120
123
121−import Data.Swagger.Lens
124+import Data.OpenApi.Lens
122−import Data.Swagger.Optics ()
125+import Data.OpenApi.Optics ()
123−import Data.Swagger.Operation
126+import Data.OpenApi.Operation
124−import Data.Swagger.ParamSchema
127+import Data.OpenApi.ParamSchema
125−import Data.Swagger.Schema
128+import Data.OpenApi.Schema
126−import Data.Swagger.Schema.Validation
129+import Data.OpenApi.Schema.Validation
127
130
128−import Data.Swagger.Internal
131+import Data.OpenApi.Internal
129
132
130-- $setup
133-- $setup
131-- >>> import Control.Lens
134-- >>> import Control.Lens
@@ -133,6 +136,8 @@ import Data.Swagger.Internal
133-- >>> import Data.Monoid
136-- >>> import Data.Monoid
134-- >>> import Data.Proxy
137-- >>> import Data.Proxy
135-- >>> import GHC.Generics
138-- >>> import GHC.Generics
139+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
140+-- >>> import Data.OpenApi.Internal.Utils
136-- >>> :set -XDeriveGeneric
141-- >>> :set -XDeriveGeneric
137-- >>> :set -XOverloadedStrings
142-- >>> :set -XOverloadedStrings
138-- >>> :set -XOverloadedLists
143-- >>> :set -XOverloadedLists
@@ -149,8 +154,16 @@ import Data.Swagger.Internal
149--
154--
150-- In this library you can use @'mempty'@ for a default/empty value. For instance:
155-- In this library you can use @'mempty'@ for a default/empty value. For instance:
151--
156--
152−-- >>> encode (mempty :: Swagger)
157+-- >>> BSL.putStrLn $ encodePretty (mempty :: OpenApi)
153−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"}}"
158+-- {
159+-- "components": {},
160+-- "info": {
161+-- "title": "",
162+-- "version": ""
163+-- },
164+-- "openapi": "3.0.0",
165+-- "paths": {}
166+-- }
154--
167--
155-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
168-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
156-- That is because these properties are actually required ones.
169-- That is because these properties are actually required ones.
@@ -158,47 +171,87 @@ import Data.Swagger.Internal
158-- You /should/ always override the default (empty) value for these properties,
171-- You /should/ always override the default (empty) value for these properties,
159-- although it is not strictly necessary:
172-- although it is not strictly necessary:
160--
173--
161−-- >>> encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
174+-- >>> BSL.putStrLn $ encodePretty mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
175+-- {
176+-- "title": "Todo API",
162−-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
177+-- "version": "1.0"
178+-- }
163--
179--
164-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
180-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
165--
181--
166−-- >>> encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
182+-- >>> BSL.putStrLn $ encodePretty $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
183+-- {
167−-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
184+-- "title": "Todo API",
185+-- "version": "1.0"
186+-- }
187+
168--
188--
169-- This can be useful for combining specifications of endpoints into a whole API specification:
189-- This can be useful for combining specifications of endpoints into a whole API specification:
170--
190--
171-- @
191-- @
172-- \-\- /account subAPI specification
192-- \-\- /account subAPI specification
173−-- accountAPI :: Swagger
193+-- accountAPI :: OpenApi
174--
194--
175-- \-\- /task subAPI specification
195-- \-\- /task subAPI specification
176−-- taskAPI :: Swagger
196+-- taskAPI :: OpenApi
177--
197--
178-- \-\- while API specification is just a combination
198-- \-\- while API specification is just a combination
179-- \-\- of subAPIs' specifications
199-- \-\- of subAPIs' specifications
180−-- api :: Swagger
200+-- api :: OpenApi
181-- api = accountAPI <> taskAPI
201-- api = accountAPI <> taskAPI
182-- @
202-- @
183
203
184-- $lens
204-- $lens
185--
205--
186−-- Note: if you're working with the <https://hackage.haskell.org/package/optics optics> library, take a look at "Data.Swagger.Optics".
206+-- Note: if you're working with the <https://hackage.haskell.org/package/optics optics> library, take a look at "Data.OpenApi.Optics".
187--
207--
188-- Since @'Swagger'@ has a fairly complex structure, lenses and prisms are used
208-- Since @'Swagger'@ has a fairly complex structure, lenses and prisms are used
189-- to work comfortably with it. In combination with @'Monoid'@ instances, lenses
209-- to work comfortably with it. In combination with @'Monoid'@ instances, lenses
190-- make it fairly simple to construct/modify any part of the specification:
210-- make it fairly simple to construct/modify any part of the specification:
191--
211--
192-- >>> :{
212-- >>> :{
193−-- encode $ (mempty :: Swagger)
213+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
194−-- & definitions .~ [ ("User", mempty & type_ ?~ SwaggerString) ]
214+-- & components . schemas .~ [ ("User", mempty & type_ ?~ OpenApiString) ]
195-- & paths .~
215-- & paths .~
196-- [ ("/user", mempty & get ?~ (mempty
216-- [ ("/user", mempty & get ?~ (mempty
197−-- & produces ?~ MimeList ["application/json"]
198−-- & at 200 ?~ ("OK" & _Inline.schema ?~ Ref (Reference "User"))
217+-- & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User")))
199-- & at 404 ?~ "User info not found")) ]
218-- & at 404 ?~ "User info not found")) ]
200-- :}
219-- :}
201−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"produces\":[\"application/json\"],\"responses\":{\"404\":{\"description\":\"User info not found\"},\"200\":{\"schema\":{\"$ref\":\"#/definitions/User\"},\"description\":\"OK\"}}}}},\"definitions\":{\"User\":{\"type\":\"string\"}}}"
220+-- {
221+-- "components": {
222+-- "schemas": {
223+-- "User": {
224+-- "type": "string"
225+-- }
226+-- }
227+-- },
228+-- "info": {
229+-- "title": "",
230+-- "version": ""
231+-- },
232+-- "openapi": "3.0.0",
233+-- "paths": {
234+-- "/user": {
235+-- "get": {
236+-- "responses": {
237+-- "200": {
238+-- "content": {
239+-- "application/json": {
240+-- "schema": {
241+-- "$ref": "#/components/schemas/User"
242+-- }
243+-- }
244+-- },
245+-- "description": "OK"
246+-- },
247+-- "404": {
248+-- "description": "User info not found"
249+-- }
250+-- }
251+-- }
252+-- }
253+-- }
254+-- }
202--
255--
203-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
256-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
204-- which produces @application/json@ output. It should respond with code @200@ and body specified
257-- which produces @application/json@ output. It should respond with code @200@ and body specified
@@ -210,30 +263,34 @@ import Data.Swagger.Internal
210-- common field is @'description'@. Many components of a Swagger specification
263-- common field is @'description'@. Many components of a Swagger specification
211-- can have descriptions, and you can use the same name for them:
264-- can have descriptions, and you can use the same name for them:
212--
265--
213−-- >>> encode $ (mempty :: Response) & description .~ "No content"
266+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & description .~ "No content"
267+-- {
214−-- "{\"description\":\"No content\"}"
268+-- "description": "No content"
269+-- }
215-- >>> :{
270-- >>> :{
216−-- encode $ (mempty :: Schema)
271+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
217−-- & type_ ?~ SwaggerBoolean
272+-- & type_ ?~ OpenApiBoolean
218-- & description ?~ "To be or not to be"
273-- & description ?~ "To be or not to be"
219-- :}
274-- :}
275+-- {
220−-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
276+-- "description": "To be or not to be",
277+-- "type": "boolean"
221−--
278+-- }
222−-- @'ParamSchema'@ is basically the /base schema specification/ and many types contain it (see @'HasParamSchema'@).
223−-- So for convenience, all @'ParamSchema'@ fields are transitively made fields of the type that has it.
224−-- For example, you can use @'type_'@ to access @'SwaggerType'@ of @'Header'@ schema without having to use @'paramSchema'@:
225−--
226−-- >>> encode $ (mempty :: Header) & type_ ?~ SwaggerNumber
227−-- "{\"type\":\"number\"}"
228--
279--
229-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
280-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
230-- have direct access to it via @'at' code@. Example:
281-- have direct access to it via @'at' code@. Example:
231--
282--
232-- >>> :{
283-- >>> :{
233−-- encode $ (mempty :: Operation)
284+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
234-- & at 404 ?~ "Not found"
285-- & at 404 ?~ "Not found"
235-- :}
286-- :}
287+-- {
288+-- "responses": {
289+-- "404": {
236−-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
290+-- "description": "Not found"
291+-- }
292+-- }
293+-- }
237--
294--
238-- You might've noticed that @'type_'@ has an extra underscore in its name
295-- You might've noticed that @'type_'@ has an extra underscore in its name
239-- compared to, say, @'description'@ field accessor.
296-- compared to, say, @'description'@ field accessor.
@@ -249,7 +306,7 @@ import Data.Swagger.Internal
249-- @'ParamSchema'@ and @'Schema'@ are the two core types for data model specification.
306-- @'ParamSchema'@ and @'Schema'@ are the two core types for data model specification.
250--
307--
251-- @'ParamSchema' t@ specifies all the common properties, available for every data schema.
308-- @'ParamSchema' t@ specifies all the common properties, available for every data schema.
252−-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'SwaggerType'@ and @'SwaggerItems'@).
309+-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'OpenApiType'@ and @'OpenApiItems'@).
253--
310--
254-- @'Schema'@ is used for request and response bodies and allows specifying objects
311-- @'Schema'@ is used for request and response bodies and allows specifying objects
255-- with properties in addition to what @'ParamSchema'@ provides.
312-- with properties in addition to what @'ParamSchema'@ provides.
@@ -280,45 +337,81 @@ import Data.Swagger.Internal
280-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
337-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
281-- >>> instance ToJSON Person
338-- >>> instance ToJSON Person
282-- >>> instance ToSchema Person
339-- >>> instance ToSchema Person
283−-- >>> encode (Person "David" 28)
340+-- >>> BSL.putStrLn $ encodePretty (Person "David" 28)
341+-- {
342+-- "age": 28,
284−-- "{\"age\":28,\"name\":\"David\"}"
343+-- "name": "David"
344+-- }
285−-- >>> encode $ toSchema (Proxy :: Proxy Person)
345+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Person)
286−-- "{\"required\":[\"name\",\"age\"],\"properties\":{\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"}},\"type\":\"object\"}"
346+-- {
347+-- "properties": {
348+-- "age": {
349+-- "type": "integer"
350+-- },
351+-- "name": {
352+-- "type": "string"
353+-- }
354+-- },
355+-- "required": [
356+-- "name",
357+-- "age"
358+-- ],
359+-- "type": "object"
360+-- }
287−--
361+--
288−-- Please note that not all valid Haskell data types will have a proper swagger schema. For example while we can derive a
362+-- This package implements OpenAPI 3.0 spec, which supports @oneOf@ in schemas, allowing any sum types
289−-- schema for basic enums like
363+-- to be faithfully represented. All sum encodings supported by @aeson@ are supported here as well, with
364+-- an exception of 'Data.Aeson.TwoElemArray', since OpenAPI spec does not support heterogeneous arrays.
365+--
366+-- An example with 'Data.Aeson.TaggedObject' encoding:
290−--
367+--
291−-- >>> data SampleEnum = ChoiceOne | ChoiceTwo deriving Generic
368+-- >>> data Error = ErrorNoUser { userId :: Int } | ErrorAccessDenied { requiredPermission :: String } deriving Generic
292−-- >>> instance ToSchema SampleEnum
369+-- >>> instance ToJSON Error
293−-- >>> instance ToJSON SampleEnum
370+-- >>> instance ToSchema Error
294−--
371+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Error)
295−-- and for sum types that have constructors with values
372+-- {
296−--
373+-- "oneOf": [
297−-- >>> data SampleSumType = ChoiceInt Int | ChoiceString String deriving Generic
298−-- >>> instance ToSchema SampleSumType
299−-- >>> instance ToJSON SampleSumType
300−--
301−-- we can not derive a valid schema for a mix of the above. The following will result in a type error
302−--
374+-- {
303−-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
375+-- "properties": {
304−-- >>> instance ToSchema BadMixedType
376+-- "tag": {
305−-- ...
377+-- "enum": [
306−-- ... error:
378+-- "ErrorNoUser"
307−-- ... • Cannot derive Generic-based Swagger Schema for BadMixedType
379+-- ],
308−-- ... BadMixedType is a mixed sum type (has both unit and non-unit constructors).
380+-- "type": "string"
309−-- ... Swagger does not have a good representation for these types.
381+-- },
310−-- ... Use genericDeclareNamedSchemaUnrestricted if you want to derive schema
382+-- "userId": {
311−-- ... that matches aeson's Generic-based toJSON,
383+-- "maximum": 9223372036854775807,
312−-- ... but that's not supported by some Swagger tools.
384+-- "minimum": -9223372036854775808,
313−-- ...
385+-- "type": "integer"
314−-- ... In the instance declaration for ‘ToSchema BadMixedType’
386+-- }
387+-- },
388+-- "required": [
389+-- "userId",
390+-- "tag"
391+-- ],
392+-- "type": "object"
393+-- },
394+-- {
395+-- "properties": {
396+-- "requiredPermission": {
397+-- "type": "string"
398+-- },
399+-- "tag": {
400+-- "enum": [
401+-- "ErrorAccessDenied"
315−--
402+-- ],
316−-- We can use 'genericDeclareNamedSchemaUnrestricted' to try our best to represent this type as a Swagger Schema and match 'ToJSON':
403+-- "type": "string"
404+-- }
405+-- },
406+-- "required": [
407+-- "requiredPermission",
408+-- "tag"
317−--
409+-- ],
318−-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
410+-- "type": "object"
319−-- >>> instance ToSchema BadMixedType where declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
411+-- }
320−-- >>> instance ToJSON BadMixedType
412+-- ],
413+-- "type": "object"
321−--
414+-- }
322
415
323-- $manipulation
416-- $manipulation
324-- Sometimes you have to work with an imported or generated @'Swagger'@.
417-- Sometimes you have to work with an imported or generated @'Swagger'@.
@@ -326,10 +419,10 @@ import Data.Swagger.Internal
326-- for a type-level servant API.
419-- for a type-level servant API.
327--
420--
328-- Lenses and prisms can be used to manipulate such specification to add additional information, tags, extra responses, etc.
421-- Lenses and prisms can be used to manipulate such specification to add additional information, tags, extra responses, etc.
329−-- To facilitate common needs, @"Data.Swagger.Operation"@ module provides useful helpers.
422+-- To facilitate common needs, @"Data.OpenApi.Operation"@ module provides useful helpers.
330
423
331-- $validation
424-- $validation
332-- While @'ToParamSchema'@ and @'ToSchema'@ provide means to easily obtain schemas for Haskell types,
425-- While @'ToParamSchema'@ and @'ToSchema'@ provide means to easily obtain schemas for Haskell types,
333-- there is no static mechanism to ensure those instances correspond to the @'ToHttpApiData'@ or @'ToJSON'@ instances.
426-- there is no static mechanism to ensure those instances correspond to the @'ToHttpApiData'@ or @'ToJSON'@ instances.
334--
427--
335−-- @"Data.Swagger.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.
428+-- @"Data.OpenApi.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.
src/Data/OpenApi/Aeson/Compat.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE CPP #-}
2+
3+module Data.OpenApi.Aeson.Compat where
4+
5+#if MIN_VERSION_aeson(2,0,0)
6+import Data.Aeson (Key)
7+import qualified Data.Aeson.Key as Key
8+import qualified Data.Aeson.KeyMap as KeyMap
9+#else
10+import qualified Data.HashMap.Strict as HM
11+#endif
12+import Data.Bifunctor (first)
13+import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
14+import qualified Data.Text as T
15+
16+#if MIN_VERSION_aeson(2,0,0)
17+deleteKey :: Key -> KeyMap.KeyMap v -> KeyMap.KeyMap v
18+deleteKey = KeyMap.delete
19+
20+objectToList :: KeyMap.KeyMap v -> [(Key, v)]
21+objectToList = KeyMap.toList
22+
23+objectKeys :: KeyMap.KeyMap v -> [T.Text]
24+objectKeys = map Key.toText . KeyMap.keys
25+
26+stringToKey :: String -> Key
27+stringToKey = Key.fromString
28+
29+keyToString :: Key -> String
30+keyToString = Key.toString
31+
32+keyToText :: Key -> T.Text
33+keyToText = Key.toText
34+
35+toInsOrdHashMap :: KeyMap.KeyMap v -> InsOrdHashMap.InsOrdHashMap T.Text v
36+toInsOrdHashMap = InsOrdHashMap.fromList . fmap (first Key.toText) . KeyMap.toList
37+
38+fromInsOrdHashMap :: InsOrdHashMap.InsOrdHashMap T.Text v -> KeyMap.KeyMap v
39+fromInsOrdHashMap = KeyMap.fromList . fmap (first Key.fromText) . InsOrdHashMap.toList
40+
41+lookupKey :: T.Text -> KeyMap.KeyMap v -> Maybe v
42+lookupKey = KeyMap.lookup . Key.fromText
43+
44+hasKey :: T.Text -> KeyMap.KeyMap a -> Bool
45+hasKey = KeyMap.member . Key.fromText
46+#else
47+deleteKey :: T.Text -> HM.HashMap T.Text v -> HM.HashMap T.Text v
48+deleteKey = HM.delete
49+
50+objectToList :: HM.HashMap T.Text v -> [(T.Text, v)]
51+objectToList = HM.toList
52+
53+objectKeys :: HM.HashMap T.Text v -> [T.Text]
54+objectKeys = HM.keys
55+
56+stringToKey :: String -> T.Text
57+stringToKey = T.pack
58+
59+keyToString :: T.Text -> String
60+keyToString = T.unpack
61+
62+keyToText :: T.Text -> T.Text
63+keyToText = id
64+
65+toInsOrdHashMap :: HM.HashMap T.Text v -> InsOrdHashMap.InsOrdHashMap T.Text v
66+toInsOrdHashMap = InsOrdHashMap.fromHashMap
67+
68+fromInsOrdHashMap :: InsOrdHashMap.InsOrdHashMap T.Text v -> HM.HashMap T.Text v
69+fromInsOrdHashMap = InsOrdHashMap.toHashMap
70+
71+lookupKey :: T.Text -> HM.HashMap T.Text v -> Maybe v
72+lookupKey = HM.lookup
73+
74+hasKey :: T.Text -> HM.HashMap T.Text a -> Bool
75+hasKey = HM.member
76+#endif
src/Data/Swagger/Declare.hs → src/Data/OpenApi/Declare.hsrenamed+2−2
@@ -4,12 +4,12 @@
4{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
6-- |
6-- |
7−-- Module: Data.Swagger.Declare
7+-- Module: Data.OpenApi.Declare
8-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
8-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
9-- Stability: experimental
9-- Stability: experimental
10--
10--
11-- Declare monad transformer and associated functions.
11-- Declare monad transformer and associated functions.
12−module Data.Swagger.Declare where
12+module Data.OpenApi.Declare where
13
13
14import Prelude ()
14import Prelude ()
15import Prelude.Compat
15import Prelude.Compat
src/Data/Swagger/Internal.hs → src/Data/OpenApi/Internal.hsrenamed+470−406
@@ -1,6 +1,6 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveGeneric #-}
4{-# LANGUAGE DeriveGeneric #-}
5{-# LANGUAGE DeriveTraversable #-}
5{-# LANGUAGE DeriveTraversable #-}
6{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleContexts #-}
@@ -14,87 +14,91 @@
14{-# LANGUAGE TemplateHaskell #-}
14{-# LANGUAGE TemplateHaskell #-}
15{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE UndecidableInstances #-}
16{-# LANGUAGE UndecidableInstances #-}
17−module Data.Swagger.Internal where
17+module Data.OpenApi.Internal where
18
18
19import Prelude ()
19import Prelude ()
20import Prelude.Compat
20import Prelude.Compat
21
21
22−import Control.Lens ((&), (.~), (?~))
23import Control.Applicative
22import Control.Applicative
23+import Control.Lens ((&), (.~), (?~))
24−import Data.Aeson hiding (Encoding)
24+import Data.Aeson hiding (Encoding)
25+#if MIN_VERSION_aeson(2,0,0)
26+import qualified Data.Aeson.KeyMap as KeyMap
27+#endif
25−import qualified Data.Aeson.Types as JSON
28+import qualified Data.Aeson.Types as JSON
26−import Data.Data (Data(..), Typeable, mkConstr, mkDataType, Fixity(..), Constr, DataType, constrIndex)
29+import Data.Data (Constr, Data (..), DataType, Fixity (..), Typeable,
30+ constrIndex, mkConstr, mkDataType)
27−import Data.Hashable (Hashable)
31+import Data.Hashable (Hashable (..))
28−import qualified Data.HashMap.Strict as HashMap
32+import qualified Data.HashMap.Strict as HashMap
29−import Data.HashSet.InsOrd (InsOrdHashSet)
33+import Data.HashSet.InsOrd (InsOrdHashSet)
30−import Data.Map (Map)
34+import Data.Map (Map)
31−import qualified Data.Map as Map
35+import qualified Data.Map as Map
32−import Data.Monoid (Monoid (..))
36+import Data.Monoid (Monoid (..))
37+import Data.Scientific (Scientific)
33−import Data.Semigroup.Compat (Semigroup (..))
38+import Data.Semigroup.Compat (Semigroup (..))
34−import Data.Scientific (Scientific)
35−import Data.String (IsString(..))
39+import Data.String (IsString (..))
36−import Data.Text (Text)
40+import Data.Text (Text)
37−import qualified Data.Text as Text
41+import qualified Data.Text as Text
42+import Data.Text.Encoding (encodeUtf8)
38−import GHC.Generics (Generic)
43+import GHC.Generics (Generic)
44+import Network.HTTP.Media (MediaType, mainType, parameters, parseAccept, subType, (//),
45+ (/:))
39−import Network.Socket (HostName, PortNumber)
46+import Network.Socket (HostName, PortNumber)
40−import Network.HTTP.Media (MediaType)
41−import Text.Read (readMaybe)
47+import Text.Read (readMaybe)
42
48
43import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
49import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
44import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
50import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
45
51
52+import Data.OpenApi.Aeson.Compat (deleteKey)
53+import Data.OpenApi.Internal.AesonUtils (AesonDefaultValue (..), HasSwaggerAesonOptions (..),
54+ mkSwaggerAesonOptions, saoAdditionalPairs, saoSubObject,
55+ sopSwaggerGenericParseJSON, sopSwaggerGenericToEncoding,
56+ sopSwaggerGenericToJSON, sopSwaggerGenericToJSONWithOpts)
57+import Data.OpenApi.Internal.Utils
46import Generics.SOP.TH (deriveGeneric)
58import Generics.SOP.TH (deriveGeneric)
47−import Data.Swagger.Internal.AesonUtils (sopSwaggerGenericToJSON
48− ,sopSwaggerGenericToJSONWithOpts
49− ,sopSwaggerGenericParseJSON
50− ,HasSwaggerAesonOptions(..)
51− ,AesonDefaultValue(..)
52− ,mkSwaggerAesonOptions
53− ,saoAdditionalPairs
54− ,saoSubObject)
55−import Data.Swagger.Internal.Utils
56−import Data.Swagger.Internal.AesonUtils (sopSwaggerGenericToEncoding)
57
59
58-- $setup
60-- $setup
59-- >>> :seti -XDataKinds
61-- >>> :seti -XDataKinds
60-- >>> import Data.Aeson
62-- >>> import Data.Aeson
63+-- >>> import Data.ByteString.Lazy.Char8 as BSL
64+-- >>> import Data.OpenApi.Internal.Utils
61
65
62-- | A list of definitions that can be used in references.
66-- | A list of definitions that can be used in references.
63type Definitions = InsOrdHashMap Text
67type Definitions = InsOrdHashMap Text
64
68
65-- | This is the root document object for the API specification.
69-- | This is the root document object for the API specification.
66−data Swagger = Swagger
70+data OpenApi = OpenApi
67 { -- | Provides metadata about the API.
71 { -- | Provides metadata about the API.
68 -- The metadata can be used by the clients if needed.
72 -- The metadata can be used by the clients if needed.
69− _swaggerInfo :: Info
73+ _openApiInfo :: Info
70
74
71 -- | An array of Server Objects, which provide connectivity information
75 -- | An array of Server Objects, which provide connectivity information
72 -- to a target server. If the servers property is not provided, or is an empty array,
76 -- to a target server. If the servers property is not provided, or is an empty array,
73 -- the default value would be a 'Server' object with a url value of @/@.
77 -- the default value would be a 'Server' object with a url value of @/@.
74− , _swaggerServers :: [Server]
78+ , _openApiServers :: [Server]
75
79
76 -- | The available paths and operations for the API.
80 -- | The available paths and operations for the API.
77− , _swaggerPaths :: InsOrdHashMap FilePath PathItem
81+ , _openApiPaths :: InsOrdHashMap FilePath PathItem
78
82
79 -- | An element to hold various schemas for the specification.
83 -- | An element to hold various schemas for the specification.
80− , _swaggerComponents :: Components
84+ , _openApiComponents :: Components
81
85
82 -- | A declaration of which security mechanisms can be used across the API.
86 -- | A declaration of which security mechanisms can be used across the API.
83 -- The list of values includes alternative security requirement objects that can be used.
87 -- The list of values includes alternative security requirement objects that can be used.
84 -- Only one of the security requirement objects need to be satisfied to authorize a request.
88 -- Only one of the security requirement objects need to be satisfied to authorize a request.
85 -- Individual operations can override this definition.
89 -- Individual operations can override this definition.
86 -- To make security optional, an empty security requirement can be included in the array.
90 -- To make security optional, an empty security requirement can be included in the array.
87− , _swaggerSecurity :: [SecurityRequirement]
91+ , _openApiSecurity :: [SecurityRequirement]
88
92
89 -- | A list of tags used by the specification with additional metadata.
93 -- | A list of tags used by the specification with additional metadata.
90 -- The order of the tags can be used to reflect on their order by the parsing tools.
94 -- The order of the tags can be used to reflect on their order by the parsing tools.
91 -- Not all tags that are used by the 'Operation' Object must be declared.
95 -- Not all tags that are used by the 'Operation' Object must be declared.
92 -- The tags that are not declared MAY be organized randomly or based on the tools' logic.
96 -- The tags that are not declared MAY be organized randomly or based on the tools' logic.
93 -- Each tag name in the list MUST be unique.
97 -- Each tag name in the list MUST be unique.
94− , _swaggerTags :: InsOrdHashSet Tag
98+ , _openApiTags :: InsOrdHashSet Tag
95
99
96 -- | Additional external documentation.
100 -- | Additional external documentation.
97− , _swaggerExternalDocs :: Maybe ExternalDocs
101+ , _openApiExternalDocs :: Maybe ExternalDocs
98 } deriving (Eq, Show, Generic, Data, Typeable)
102 } deriving (Eq, Show, Generic, Data, Typeable)
99
103
100-- | The object provides metadata about the API.
104-- | The object provides metadata about the API.
@@ -191,10 +195,10 @@ data Components = Components
191 , _componentsParameters :: Definitions Param
195 , _componentsParameters :: Definitions Param
192 , _componentsExamples :: Definitions Example
196 , _componentsExamples :: Definitions Example
193 , _componentsRequestBodies :: Definitions RequestBody
197 , _componentsRequestBodies :: Definitions RequestBody
194− , _componentsHeader :: Definitions Header
198+ , _componentsHeaders :: Definitions Header
195− , _componentsSecuritySchemes :: Definitions SecurityScheme
199+ , _componentsSecuritySchemes :: SecurityDefinitions
196−-- , _componentsLinks
200+ , _componentsLinks :: Definitions Link
197−-- , _componentsCallbacks
201+ , _componentsCallbacks :: Definitions Callback
198 } deriving (Eq, Show, Generic, Data, Typeable)
202 } deriving (Eq, Show, Generic, Data, Typeable)
199
203
200-- | Describes the operations available on a single path.
204-- | Describes the operations available on a single path.
@@ -284,7 +288,11 @@ data Operation = Operation
284 -- | The list of possible responses as they are returned from executing this operation.
288 -- | The list of possible responses as they are returned from executing this operation.
285 , _operationResponses :: Responses
289 , _operationResponses :: Responses
286
290
287− -- TODO callbacks
291+ -- | A map of possible out-of band callbacks related to the parent operation.
292+ -- The key is a unique identifier for the 'Callback' Object.
293+ -- Each value in the map is a 'Callback' Object that describes a request
294+ -- that may be initiated by the API provider and the expected responses.
295+ , _operationCallbacks :: InsOrdHashMap Text (Referenced Callback)
288
296
289 -- | Declares this operation to be deprecated.
297 -- | Declares this operation to be deprecated.
290 -- Usage of the declared operation should be refrained.
298 -- Usage of the declared operation should be refrained.
@@ -304,6 +312,22 @@ data Operation = Operation
304 , _operationServers :: [Server]
312 , _operationServers :: [Server]
305 } deriving (Eq, Show, Generic, Data, Typeable)
313 } deriving (Eq, Show, Generic, Data, Typeable)
306
314
315+-- This instance should be in @http-media@.
316+instance Data MediaType where
317+ gunfold k z c = case constrIndex c of
318+ 1 -> k (k (k (z (\main sub params -> foldl (/:) (main // sub) (Map.toList params)))))
319+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MediaType."
320+
321+ toConstr _ = mediaTypeConstr
322+
323+ dataTypeOf _ = mediaTypeData
324+
325+mediaTypeConstr = mkConstr mediaTypeData "MediaType" [] Prefix
326+mediaTypeData = mkDataType "MediaType" [mediaTypeConstr]
327+
328+instance Hashable MediaType where
329+ hashWithSalt salt mt = salt `hashWithSalt` show mt
330+
307-- | Describes a single request body.
331-- | Describes a single request body.
308data RequestBody = RequestBody
332data RequestBody = RequestBody
309 { -- | A brief description of the request body. This could contain examples of use.
333 { -- | A brief description of the request body. This could contain examples of use.
@@ -313,9 +337,11 @@ data RequestBody = RequestBody
313 -- | The content of the request body.
337 -- | The content of the request body.
314 -- The key is a media type or media type range and the value describes it.
338 -- The key is a media type or media type range and the value describes it.
315 -- For requests that match multiple keys, only the most specific key is applicable.
339 -- For requests that match multiple keys, only the most specific key is applicable.
316− -- e.g. @text/plain@ overrides @text/*@
340+ -- e.g. @text/plain@ overrides @text/\*@
317− , _requestBodyContent :: InsOrdHashMap {-MediaType-} Text MediaTypeObject -- FIXME Data MediaType
341+ , _requestBodyContent :: InsOrdHashMap MediaType MediaTypeObject
318
342
343+ -- | Determines if the request body is required in the request.
344+ -- Defaults to 'False'.
319 , _requestBodyRequired :: Maybe Bool
345 , _requestBodyRequired :: Maybe Bool
320 } deriving (Eq, Show, Generic, Data, Typeable)
346 } deriving (Eq, Show, Generic, Data, Typeable)
321
347
@@ -338,6 +364,29 @@ data MediaTypeObject = MediaTypeObject
338 , _mediaTypeObjectEncoding :: InsOrdHashMap Text Encoding
364 , _mediaTypeObjectEncoding :: InsOrdHashMap Text Encoding
339 } deriving (Eq, Show, Generic, Data, Typeable)
365 } deriving (Eq, Show, Generic, Data, Typeable)
340
366
367+-- | In order to support common ways of serializing simple parameters, a set of style values are defined.
368+data Style
369+ = StyleMatrix
370+ -- ^ Path-style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
371+ | StyleLabel
372+ -- ^ Label style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
373+ | StyleForm
374+ -- ^ Form style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
375+ -- This option replaces @collectionFormat@ with a @csv@ (when @explode@ is false) or @multi@
376+ -- (when explode is true) value from OpenAPI 2.0.
377+ | StyleSimple
378+ -- ^ Simple style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
379+ -- This option replaces @collectionFormat@ with a @csv@ value from OpenAPI 2.0.
380+ | StyleSpaceDelimited
381+ -- ^ Space separated array values.
382+ -- This option replaces @collectionFormat@ equal to @ssv@ from OpenAPI 2.0.
383+ | StylePipeDelimited
384+ -- ^ Pipe separated array values.
385+ -- This option replaces @collectionFormat@ equal to @pipes@ from OpenAPI 2.0.
386+ | StyleDeepObject
387+ -- ^ Provides a simple way of rendering nested objects using form parameters.
388+ deriving (Eq, Show, Generic, Data, Typeable)
389+
341data Encoding = Encoding
390data Encoding = Encoding
342 { -- | The Content-Type for encoding a specific property.
391 { -- | The Content-Type for encoding a specific property.
343 -- Default value depends on the property type: for @string@
392 -- Default value depends on the property type: for @string@
@@ -345,8 +394,8 @@ data Encoding = Encoding
345 -- for other primitive types – @text/plain@; for object - @application/json@;
394 -- for other primitive types – @text/plain@; for object - @application/json@;
346 -- for array – the default is defined based on the inner type.
395 -- for array – the default is defined based on the inner type.
347 -- The value can be a specific media type (e.g. @application/json@),
396 -- The value can be a specific media type (e.g. @application/json@),
348− -- a wildcard media type (e.g. @image/*@), or a comma-separated list of the two types.
397+ -- a wildcard media type (e.g. @image/\*@), or a comma-separated list of the two types.
349− _encodingContentType :: Maybe Text
398+ _encodingContentType :: Maybe MediaType
350
399
351 -- | A map allowing additional information to be provided as headers,
400 -- | A map allowing additional information to be provided as headers,
352 -- for example @Content-Disposition@. @Content-Type@ is described separately
401 -- for example @Content-Disposition@. @Content-Type@ is described separately
@@ -355,11 +404,11 @@ data Encoding = Encoding
355 , _encodingHeaders :: InsOrdHashMap Text (Referenced Header)
404 , _encodingHeaders :: InsOrdHashMap Text (Referenced Header)
356
405
357 -- | Describes how a specific property value will be serialized depending on its type.
406 -- | Describes how a specific property value will be serialized depending on its type.
358− -- See Parameter Object for details on the style property.
407+ -- See 'Param' Object for details on the style property.
359 -- The behavior follows the same values as query parameters, including default values.
408 -- The behavior follows the same values as query parameters, including default values.
360 -- This property SHALL be ignored if the request body media type
409 -- This property SHALL be ignored if the request body media type
361 -- is not @application/x-www-form-urlencoded@.
410 -- is not @application/x-www-form-urlencoded@.
362− , _encodingStyle :: Maybe Text -- TODO enum
411+ , _encodingStyle :: Maybe Style
363
412
364 -- | When this is true, property values of type @array@ or @object@ generate
413 -- | When this is true, property values of type @array@ or @object@ generate
365 -- separate parameters for each value of the array,
414 -- separate parameters for each value of the array,
@@ -385,18 +434,15 @@ mimeListConstr :: Constr
385mimeListConstr = mkConstr mimeListDataType "MimeList" ["getMimeList"] Prefix
434mimeListConstr = mkConstr mimeListDataType "MimeList" ["getMimeList"] Prefix
386
435
387mimeListDataType :: DataType
436mimeListDataType :: DataType
388−mimeListDataType = mkDataType "Data.Swagger.MimeList" [mimeListConstr]
437+mimeListDataType = mkDataType "Data.OpenApi.MimeList" [mimeListConstr]
389
438
390instance Data MimeList where
439instance Data MimeList where
391 gunfold k z c = case constrIndex c of
440 gunfold k z c = case constrIndex c of
392− 1 -> k (z (\xs -> MimeList (map fromString xs)))
441+ 1 -> k (z (MimeList . map fromString))
393 _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MimeList."
442 _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MimeList."
394 toConstr (MimeList _) = mimeListConstr
443 toConstr (MimeList _) = mimeListConstr
395 dataTypeOf _ = mimeListDataType
444 dataTypeOf _ = mimeListDataType
396
445
397−-- TODO style
398−-- TODO example
399−
400-- | Describes a single operation parameter.
446-- | Describes a single operation parameter.
401-- A unique parameter is defined by a combination of a name and location.
447-- A unique parameter is defined by a combination of a name and location.
402data Param = Param
448data Param = Param
@@ -406,7 +452,7 @@ data Param = Param
406
452
407 -- | A brief description of the parameter.
453 -- | A brief description of the parameter.
408 -- This could contain examples of use.
454 -- This could contain examples of use.
409− -- GFM syntax can be used for rich text representation.
455+ -- CommonMark syntax MAY be used for rich text representation.
410 , _paramDescription :: Maybe Text
456 , _paramDescription :: Maybe Text
411
457
412 -- | Determines whether this parameter is mandatory.
458 -- | Determines whether this parameter is mandatory.
@@ -424,10 +470,49 @@ data Param = Param
424 -- | Sets the ability to pass empty-valued parameters.
470 -- | Sets the ability to pass empty-valued parameters.
425 -- This is valid only for 'ParamQuery' parameters and allows sending
471 -- This is valid only for 'ParamQuery' parameters and allows sending
426 -- a parameter with an empty value. Default value is @false@.
472 -- a parameter with an empty value. Default value is @false@.
427− , _paramOtherSchemaAllowEmptyValue :: Maybe Bool
473+ , _paramAllowEmptyValue :: Maybe Bool
474+
475+ -- | Determines whether the parameter value SHOULD allow reserved characters,
476+ -- as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
477+ -- @:/?#[]@!$&'()*+,;=@ to be included without percent-encoding.
478+ -- This property only applies to parameters with an '_paramIn' value of 'ParamQuery'.
479+ -- The default value is 'False'.
480+ , _paramAllowReserved :: Maybe Bool
428
481
429 -- | Parameter schema.
482 -- | Parameter schema.
430 , _paramSchema :: Maybe (Referenced Schema)
483 , _paramSchema :: Maybe (Referenced Schema)
484+
485+ -- | Describes how the parameter value will be serialized depending
486+ -- on the type of the parameter value. Default values (based on value of '_paramIn'):
487+ -- for 'ParamQuery' - 'StyleForm'; for 'ParamPath' - 'StyleSimple'; for 'ParamHeader' - 'StyleSimple';
488+ -- for 'ParamCookie' - 'StyleForm'.
489+ , _paramStyle :: Maybe Style
490+
491+ -- | When this is true, parameter values of type @array@ or @object@
492+ -- generate separate parameters for each value of the array or key-value pair of the map.
493+ -- For other types of parameters this property has no effect.
494+ -- When style is @form@, the default value is true. For all other styles, the default value is false.
495+ , _paramExplode :: Maybe Bool
496+
497+ -- | Example of the parameter's potential value.
498+ -- The example SHOULD match the specified schema and encoding properties if present.
499+ -- The '_paramExample' field is mutually exclusive of the '_paramExamples' field.
500+ -- Furthermore, if referencing a schema that contains an example, the example value
501+ -- SHALL override the example provided by the schema. To represent examples of media types
502+ -- that cannot naturally be represented in JSON or YAML, a string value can contain
503+ -- the example with escaping where necessary.
504+ , _paramExample :: Maybe Value
505+
506+ -- | Examples of the parameter's potential value.
507+ -- Each example SHOULD contain a value in the correct format as specified
508+ -- in the parameter encoding. The '_paramExamples' field is mutually exclusive of the '_paramExample' field.
509+ -- Furthermore, if referencing a schema that contains an example,
510+ -- the examples value SHALL override the example provided by the schema.
511+ , _paramExamples :: InsOrdHashMap Text (Referenced Example)
512+
513+ -- TODO
514+ -- _paramContent :: InsOrdHashMap MediaType MediaTypeObject
515+ -- should be singleton. mutually exclusive with _paramSchema.
431 } deriving (Eq, Show, Generic, Data, Typeable)
516 } deriving (Eq, Show, Generic, Data, Typeable)
432
517
433data Example = Example
518data Example = Example
@@ -452,138 +537,64 @@ data Example = Example
452 , _exampleExternalValue :: Maybe URL
537 , _exampleExternalValue :: Maybe URL
453 } deriving (Eq, Show, Generic, Typeable, Data)
538 } deriving (Eq, Show, Generic, Typeable, Data)
454
539
540+data ExpressionOrValue
541+ = Expression Text
542+ | Value Value
543+ deriving (Eq, Show, Generic, Typeable, Data)
544+
545+-- | The Link object represents a possible design-time link for a response.
546+-- The presence of a link does not guarantee the caller's ability to successfully invoke it,
547+-- rather it provides a known relationship and traversal mechanism between responses and other operations.
548+data Link = Link
549+ { -- | A relative or absolute URI reference to an OAS operation.
550+ -- This field is mutually exclusive of the '_linkOperationId' field,
551+ -- and MUST point to an 'Operation' Object. Relative '_linkOperationRef'
552+ -- values MAY be used to locate an existing 'Operation' Object in the OpenAPI definition.
553+ _linkOperationRef :: Maybe Text
554+
555+ -- | The name of an /existing/, resolvable OAS operation, as defined with a unique
556+ -- '_operationOperationId'. This field is mutually exclusive of the '_linkOperationRef' field.
557+ , _linkOperationId :: Maybe Text
558+
559+ -- | A map representing parameters to pass to an operation as specified with '_linkOperationId'
560+ -- or identified via '_linkOperationRef'. The key is the parameter name to be used, whereas
561+ -- the value can be a constant or an expression to be evaluated and passed to the linked operation.
562+ -- The parameter name can be qualified using the parameter location @[{in}.]{name}@
563+ -- for operations that use the same parameter name in different locations (e.g. path.id).
564+ , _linkParameters :: InsOrdHashMap Text ExpressionOrValue
565+
566+ -- | A literal value or @{expression}@ to use as a request body when calling the target operation.
567+ , _linkRequestBody :: Maybe ExpressionOrValue
568+
569+ -- | A description of the link.
570+ , _linkDescription :: Maybe Text
571+
572+ -- | A server object to be used by the target operation.
573+ , _linkServer :: Maybe Server
574+ } deriving (Eq, Show, Generic, Typeable, Data)
575+
455−-- | Items for @'SwaggerArray'@ schemas.
576+-- | Items for @'OpenApiArray'@ schemas.
456--
577--
457−-- @'SwaggerItemsPrimitive'@ should be used only for query params, headers and path pieces.
578+-- __Warning__: OpenAPI 3.0 does not support tuple arrays. However, OpenAPI 3.1 will, as
458−-- The @'CollectionFormat' t@ parameter specifies how elements of an array should be displayed.
579+-- it will incorporate Json Schema mostly verbatim.
459−-- Note that @fmt@ in @'SwaggerItemsPrimitive' fmt schema@ specifies format for elements of type @schema@.
460−-- This is different from the original Swagger's <http://swagger.io/specification/#itemsObject Items Object>.
461--
580--
462−-- @'SwaggerItemsObject'@ should be used to specify homogenous array @'Schema'@s.
581+-- @'OpenApiItemsObject'@ should be used to specify homogenous array @'Schema'@s.
463--
582--
464−-- @'SwaggerItemsArray'@ should be used to specify tuple @'Schema'@s.
583+-- @'OpenApiItemsArray'@ should be used to specify tuple @'Schema'@s.
465−data SwaggerItems t where
584+data OpenApiItems where
466− SwaggerItemsPrimitive :: Maybe (CollectionFormat k) -> ParamSchema k-> SwaggerItems k
585+ OpenApiItemsObject :: Referenced Schema -> OpenApiItems
467− SwaggerItemsObject :: Referenced Schema -> SwaggerItems 'SwaggerKindSchema
586+ OpenApiItemsArray :: [Referenced Schema] -> OpenApiItems
468− SwaggerItemsArray :: [Referenced Schema] -> SwaggerItems 'SwaggerKindSchema
587+ deriving (Eq, Show, Typeable, Data)
469− deriving (Typeable)
588+
470−
589+data OpenApiType where
471−deriving instance Eq (SwaggerItems t)
590+ OpenApiString :: OpenApiType
472−deriving instance Show (SwaggerItems t)
591+ OpenApiNumber :: OpenApiType
473−--deriving instance Typeable (SwaggerItems t)
592+ OpenApiInteger :: OpenApiType
474−
593+ OpenApiBoolean :: OpenApiType
475−swaggerItemsPrimitiveConstr :: Constr
594+ OpenApiArray :: OpenApiType
476−swaggerItemsPrimitiveConstr = mkConstr swaggerItemsDataType "SwaggerItemsPrimitive" [] Prefix
595+ OpenApiNull :: OpenApiType
477−
596+ OpenApiObject :: OpenApiType
478−swaggerItemsObjectConstr :: Constr
597+ deriving (Eq, Show, Typeable, Generic, Data)
479−swaggerItemsObjectConstr = mkConstr swaggerItemsDataType "SwaggerItemsObject" [] Prefix
480−
481−swaggerItemsArrayConstr :: Constr
482−swaggerItemsArrayConstr = mkConstr swaggerItemsDataType "SwaggerItemsArray" [] Prefix
483−
484−swaggerItemsDataType :: DataType
485−swaggerItemsDataType = mkDataType "Data.Swagger.SwaggerItems" [swaggerItemsPrimitiveConstr]
486−
487−-- Note: unfortunately we have to write these Data instances by hand,
488−-- to get better contexts / avoid duplicate name when using standalone deriving
489−
490−instance Data t => Data (SwaggerItems ('SwaggerKindNormal t)) where
491− -- TODO: define gfoldl
492− gunfold k z c = case constrIndex c of
493− 1 -> k (k (z SwaggerItemsPrimitive))
494− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems t)."
495− toConstr _ = swaggerItemsPrimitiveConstr
496− dataTypeOf _ = swaggerItemsDataType
497−
498−-- SwaggerItems SwaggerKindParamOtherSchema can be constructed using SwaggerItemsPrimitive only
499−instance Data (SwaggerItems 'SwaggerKindParamOtherSchema) where
500− -- TODO: define gfoldl
501− gunfold k z c = case constrIndex c of
502− 1 -> k (k (z SwaggerItemsPrimitive))
503− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems SwaggerKindParamOtherSchema)."
504− toConstr _ = swaggerItemsPrimitiveConstr
505− dataTypeOf _ = swaggerItemsDataType
506−
507−instance Data (SwaggerItems 'SwaggerKindSchema) where
508− gfoldl _ _ (SwaggerItemsPrimitive _ _) = error " Data.Data.gfoldl: Constructor SwaggerItemsPrimitive used to construct SwaggerItems SwaggerKindSchema"
509− gfoldl k z (SwaggerItemsObject ref) = z SwaggerItemsObject `k` ref
510− gfoldl k z (SwaggerItemsArray ref) = z SwaggerItemsArray `k` ref
511−
512− gunfold k z c = case constrIndex c of
513− 2 -> k (z SwaggerItemsObject)
514− 3 -> k (z SwaggerItemsArray)
515− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems SwaggerKindSchema)."
516−
517− toConstr (SwaggerItemsPrimitive _ _) = error "Not supported"
518− toConstr (SwaggerItemsObject _) = swaggerItemsObjectConstr
519− toConstr (SwaggerItemsArray _) = swaggerItemsArrayConstr
520−
521− dataTypeOf _ = swaggerItemsDataType
522−
523−-- | Type used as a kind to avoid overlapping instances.
524−data SwaggerKind t
525− = SwaggerKindNormal t
526− | SwaggerKindParamOtherSchema
527− | SwaggerKindSchema
528− deriving (Typeable)
529−
530−deriving instance Typeable 'SwaggerKindNormal
531−deriving instance Typeable 'SwaggerKindParamOtherSchema
532−deriving instance Typeable 'SwaggerKindSchema
533−
534−-- TODO remove
535−type family SwaggerKindType (k :: SwaggerKind *) :: *
536−type instance SwaggerKindType ('SwaggerKindNormal t) = t
537−type instance SwaggerKindType 'SwaggerKindSchema = Schema
538−--type instance SwaggerKindType 'SwaggerKindParamOtherSchema = ParamOtherSchema
539−
540−data SwaggerType t where
541− SwaggerString :: SwaggerType t
542− SwaggerNumber :: SwaggerType t
543− SwaggerInteger :: SwaggerType t
544− SwaggerBoolean :: SwaggerType t
545− SwaggerArray :: SwaggerType t
546− SwaggerFile :: SwaggerType 'SwaggerKindParamOtherSchema
547− SwaggerNull :: SwaggerType 'SwaggerKindSchema
548− SwaggerObject :: SwaggerType 'SwaggerKindSchema
549− deriving (Typeable)
550−
551−deriving instance Eq (SwaggerType t)
552−deriving instance Show (SwaggerType t)
553−
554−swaggerTypeConstr :: Data (SwaggerType t) => SwaggerType t -> Constr
555−swaggerTypeConstr t = mkConstr (dataTypeOf t) (show t) [] Prefix
556−
557−swaggerTypeDataType :: {- Data (SwaggerType t) => -} SwaggerType t -> DataType
558−swaggerTypeDataType _ = mkDataType "Data.Swagger.SwaggerType" swaggerTypeConstrs
559−
560−swaggerCommonTypes :: [SwaggerType k]
561−swaggerCommonTypes = [SwaggerString, SwaggerNumber, SwaggerInteger, SwaggerBoolean, SwaggerArray]
562−
563−swaggerParamTypes :: [SwaggerType 'SwaggerKindParamOtherSchema]
564−swaggerParamTypes = swaggerCommonTypes ++ [SwaggerFile]
565−
566−swaggerSchemaTypes :: [SwaggerType 'SwaggerKindSchema]
567−swaggerSchemaTypes = swaggerCommonTypes ++ [error "SwaggerFile is invalid SwaggerType Schema", SwaggerNull, SwaggerObject]
568−
569−swaggerTypeConstrs :: [Constr]
570−swaggerTypeConstrs = map swaggerTypeConstr (swaggerCommonTypes :: [SwaggerType 'SwaggerKindSchema])
571− ++ [swaggerTypeConstr SwaggerFile, swaggerTypeConstr SwaggerNull, swaggerTypeConstr SwaggerObject]
572−
573−instance Typeable t => Data (SwaggerType ('SwaggerKindNormal t)) where
574− gunfold = gunfoldEnum "SwaggerType" swaggerCommonTypes
575− toConstr = swaggerTypeConstr
576− dataTypeOf = swaggerTypeDataType
577−
578−instance Data (SwaggerType 'SwaggerKindParamOtherSchema) where
579− gunfold = gunfoldEnum "SwaggerType ParamOtherSchema" swaggerParamTypes
580− toConstr = swaggerTypeConstr
581− dataTypeOf = swaggerTypeDataType
582−
583−instance Data (SwaggerType 'SwaggerKindSchema) where
584− gunfold = gunfoldEnum "SwaggerType Schema" swaggerSchemaTypes
585− toConstr = swaggerTypeConstr
586− dataTypeOf = swaggerTypeDataType
587
598
588data ParamLocation
599data ParamLocation
589 = -- | Parameters that are appended to the URL.
600 = -- | Parameters that are appended to the URL.
@@ -601,42 +612,6 @@ data ParamLocation
601
612
602type Format = Text
613type Format = Text
603
614
604−-- | Determines the format of the array.
605−data CollectionFormat t where
606− -- Comma separated values: @foo,bar@.
607− CollectionCSV :: CollectionFormat t
608− -- Space separated values: @foo bar@.
609− CollectionSSV :: CollectionFormat t
610− -- Tab separated values: @foo\\tbar@.
611− CollectionTSV :: CollectionFormat t
612− -- Pipe separated values: @foo|bar@.
613− CollectionPipes :: CollectionFormat t
614− -- Corresponds to multiple parameter instances
615− -- instead of multiple values for a single instance @foo=bar&foo=baz@.
616− -- This is valid only for parameters in @'ParamQuery'@ or @'ParamFormData'@.
617− CollectionMulti :: CollectionFormat 'SwaggerKindParamOtherSchema
618− deriving (Typeable)
619−
620−deriving instance Eq (CollectionFormat t)
621−deriving instance Show (CollectionFormat t)
622−
623−collectionFormatConstr :: CollectionFormat t -> Constr
624−collectionFormatConstr cf = mkConstr collectionFormatDataType (show cf) [] Prefix
625−
626−collectionFormatDataType :: DataType
627−collectionFormatDataType = mkDataType "Data.Swagger.CollectionFormat" $
628− map collectionFormatConstr collectionCommonFormats
629−
630−collectionCommonFormats :: [CollectionFormat t]
631−collectionCommonFormats = [ CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes ]
632−
633−instance Data t => Data (CollectionFormat ('SwaggerKindNormal t)) where
634− gunfold = gunfoldEnum "CollectionFormat" collectionCommonFormats
635− toConstr = collectionFormatConstr
636− dataTypeOf _ = collectionFormatDataType
637−
638−deriving instance Data (CollectionFormat 'SwaggerKindParamOtherSchema)
639−
640type ParamName = Text
615type ParamName = Text
641
616
642data Schema = Schema
617data Schema = Schema
@@ -663,9 +638,33 @@ data Schema = Schema
663 , _schemaMaxProperties :: Maybe Integer
638 , _schemaMaxProperties :: Maybe Integer
664 , _schemaMinProperties :: Maybe Integer
639 , _schemaMinProperties :: Maybe Integer
665
640
666− , _schemaParamSchema :: ParamSchema 'SwaggerKindSchema
641+ , -- | Declares the value of the parameter that the server will use if none is provided,
642+ -- for example a @"count"@ to control the number of results per page might default to @100@
643+ -- if not supplied by the client in the request.
644+ -- (Note: "default" has no meaning for required parameters.)
645+ -- Unlike JSON Schema this value MUST conform to the defined type for this parameter.
646+ _schemaDefault :: Maybe Value
647+
648+ , _schemaType :: Maybe OpenApiType
649+ , _schemaFormat :: Maybe Format
650+ , _schemaItems :: Maybe OpenApiItems
651+ , _schemaMaximum :: Maybe Scientific
652+ , _schemaExclusiveMaximum :: Maybe Bool
653+ , _schemaMinimum :: Maybe Scientific
654+ , _schemaExclusiveMinimum :: Maybe Bool
655+ , _schemaMaxLength :: Maybe Integer
656+ , _schemaMinLength :: Maybe Integer
657+ , _schemaPattern :: Maybe Pattern
658+ , _schemaMaxItems :: Maybe Integer
659+ , _schemaMinItems :: Maybe Integer
660+ , _schemaUniqueItems :: Maybe Bool
661+ , _schemaEnum :: Maybe [Value]
662+ , _schemaMultipleOf :: Maybe Scientific
667 } deriving (Eq, Show, Generic, Data, Typeable)
663 } deriving (Eq, Show, Generic, Data, Typeable)
668
664
665+-- | Regex pattern for @string@ type.
666+type Pattern = Text
667+
669data Discriminator = Discriminator
668data Discriminator = Discriminator
670 { -- | The name of the property in the payload that will hold the discriminator value.
669 { -- | The name of the property in the payload that will hold the discriminator value.
671 _discriminatorPropertyName :: Text
670 _discriminatorPropertyName :: Text
@@ -681,41 +680,9 @@ data NamedSchema = NamedSchema
681 , _namedSchemaSchema :: Schema
680 , _namedSchemaSchema :: Schema
682 } deriving (Eq, Show, Generic, Data, Typeable)
681 } deriving (Eq, Show, Generic, Data, Typeable)
683
682
684−-- | Regex pattern for @string@ type.
685−type Pattern = Text
686−
687−-- TODO examples for params
688−
689−data ParamSchema (t :: SwaggerKind *) = ParamSchema
690− { -- | Declares the value of the parameter that the server will use if none is provided,
691− -- for example a @"count"@ to control the number of results per page might default to @100@
692− -- if not supplied by the client in the request.
693− -- (Note: "default" has no meaning for required parameters.)
694− -- Unlike JSON Schema this value MUST conform to the defined type for this parameter.
695− _paramSchemaDefault :: Maybe Value
696−
697− , _paramSchemaType :: Maybe (SwaggerType t)
698− , _paramSchemaFormat :: Maybe Format
699− , _paramSchemaItems :: Maybe (SwaggerItems t)
700− , _paramSchemaMaximum :: Maybe Scientific
701− , _paramSchemaExclusiveMaximum :: Maybe Bool
702− , _paramSchemaMinimum :: Maybe Scientific
703− , _paramSchemaExclusiveMinimum :: Maybe Bool
704− , _paramSchemaMaxLength :: Maybe Integer
705− , _paramSchemaMinLength :: Maybe Integer
706− , _paramSchemaPattern :: Maybe Pattern
707− , _paramSchemaMaxItems :: Maybe Integer
708− , _paramSchemaMinItems :: Maybe Integer
709− , _paramSchemaUniqueItems :: Maybe Bool
710− , _paramSchemaEnum :: Maybe [Value]
711− , _paramSchemaMultipleOf :: Maybe Scientific
712− } deriving (Eq, Show, Generic, Typeable)
713−
714−deriving instance (Typeable k, Data (Maybe (SwaggerType k)), Data (SwaggerItems k)) => Data (ParamSchema k)
715−
716data Xml = Xml
683data Xml = Xml
717 { -- | Replaces the name of the element/attribute used for the described schema property.
684 { -- | Replaces the name of the element/attribute used for the described schema property.
718− -- When defined within the @'SwaggerItems'@ (items), it will affect the name of the individual XML elements within the list.
685+ -- When defined within the @'OpenApiItems'@ (items), it will affect the name of the individual XML elements within the list.
719 -- When defined alongside type being array (outside the items),
686 -- When defined alongside type being array (outside the items),
720 -- it will affect the wrapping element and only if wrapped is true.
687 -- it will affect the wrapping element and only if wrapped is true.
721 -- If wrapped is false, it will be ignored.
688 -- If wrapped is false, it will be ignored.
@@ -767,25 +734,46 @@ data Response = Response
767 -- | A map containing descriptions of potential response payloads.
734 -- | A map containing descriptions of potential response payloads.
768 -- The key is a media type or media type range and the value describes it.
735 -- The key is a media type or media type range and the value describes it.
769 -- For responses that match multiple keys, only the most specific key is applicable.
736 -- For responses that match multiple keys, only the most specific key is applicable.
770− -- e.g. @text/plain@ overrides @text/*@.
737+ -- e.g. @text/plain@ overrides @text/\*@.
771− , _responseContent :: InsOrdHashMap Text MediaTypeObject
738+ , _responseContent :: InsOrdHashMap MediaType MediaTypeObject
772
739
773 -- | Maps a header name to its definition.
740 -- | Maps a header name to its definition.
774 , _responseHeaders :: InsOrdHashMap HeaderName (Referenced Header)
741 , _responseHeaders :: InsOrdHashMap HeaderName (Referenced Header)
775
742
776− -- TODO links
743+ -- | A map of operations links that can be followed from the response.
744+ -- The key of the map is a short name for the link, following the naming
745+ -- constraints of the names for 'Component' Objects.
746+ , _responseLinks :: InsOrdHashMap Text (Referenced Link)
777 } deriving (Eq, Show, Generic, Data, Typeable)
747 } deriving (Eq, Show, Generic, Data, Typeable)
778
748
779instance IsString Response where
749instance IsString Response where
780− fromString s = Response (fromString s) mempty mempty
750+ fromString s = Response (fromString s) mempty mempty mempty
751+
752+-- | A map of possible out-of band callbacks related to the parent operation.
753+-- Each value in the map is a 'PathItem' Object that describes a set of requests that
754+-- may be initiated by the API provider and the expected responses.
755+-- The key value used to identify the path item object is an expression, evaluated at runtime,
756+-- that identifies a URL to use for the callback operation.
757+newtype Callback = Callback (InsOrdHashMap Text PathItem)
758+ deriving (Eq, Show, Generic, Data, Typeable)
781
759
782type HeaderName = Text
760type HeaderName = Text
783
761
762+-- | Header fields have the same meaning as for 'Param'.
763+--
764+-- Style is always treated as 'StyleSimple', as it is the only value allowed for headers.
784data Header = Header
765data Header = Header
785 { -- | A short description of the header.
766 { -- | A short description of the header.
786− _headerDescription :: Maybe Text
767+ _headerDescription :: Maybe HeaderName
768+
769+ , _headerRequired :: Maybe Bool
770+ , _headerDeprecated :: Maybe Bool
771+ , _headerAllowEmptyValue :: Maybe Bool
772+ , _headerExplode :: Maybe Bool
773+ , _headerExample :: Maybe Value
774+ , _headerExamples :: InsOrdHashMap Text (Referenced Example)
787
775
788− , _headerParamSchema :: ParamSchema ('SwaggerKindNormal Header)
776+ , _headerSchema :: Maybe (Referenced Schema)
789 } deriving (Eq, Show, Generic, Data, Typeable)
777 } deriving (Eq, Show, Generic, Data, Typeable)
790
778
791-- | The location of the API key.
779-- | The location of the API key.
@@ -809,17 +797,17 @@ type AuthorizationURL = Text
809-- | The token URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
797-- | The token URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
810type TokenURL = Text
798type TokenURL = Text
811
799
812−data OAuth2ImplicitFlow = OAuth2ImplicitFlow
800+newtype OAuth2ImplicitFlow
813− { _oAuth2ImplicitFlowAuthorizationUrl :: AuthorizationURL
801+ = OAuth2ImplicitFlow {_oAuth2ImplicitFlowAuthorizationUrl :: AuthorizationURL}
814− } deriving (Eq, Show, Generic, Data, Typeable)
802+ deriving (Eq, Show, Generic, Data, Typeable)
815
803
816−data OAuth2PasswordFlow = OAuth2PasswordFlow
804+newtype OAuth2PasswordFlow
817− { _oAuth2PasswordFlowTokenUrl :: TokenURL
805+ = OAuth2PasswordFlow {_oAuth2PasswordFlowTokenUrl :: TokenURL}
818− } deriving (Eq, Show, Generic, Data, Typeable)
806+ deriving (Eq, Show, Generic, Data, Typeable)
819
807
820−data OAuth2ClientCredentialsFlow = OAuth2ClientCredentialsFlow
808+newtype OAuth2ClientCredentialsFlow
821− { _oAuth2ClientCredentialsFlowTokenUrl :: TokenURL
809+ = OAuth2ClientCredentialsFlow {_oAuth2ClientCredentialsFlowTokenUrl :: TokenURL}
822− } deriving (Eq, Show, Generic, Data, Typeable)
810+ deriving (Eq, Show, Generic, Data, Typeable)
823
811
824data OAuth2AuthorizationCodeFlow = OAuth2AuthorizationCodeFlow
812data OAuth2AuthorizationCodeFlow = OAuth2AuthorizationCodeFlow
825 { _oAuth2AuthorizationCodeFlowAuthorizationUrl :: AuthorizationURL
813 { _oAuth2AuthorizationCodeFlowAuthorizationUrl :: AuthorizationURL
@@ -852,8 +840,50 @@ data OAuth2Flows = OAuth2Flows
852 , _oAuth2FlowsAuthorizationCode :: Maybe (OAuth2Flow OAuth2AuthorizationCodeFlow)
840 , _oAuth2FlowsAuthorizationCode :: Maybe (OAuth2Flow OAuth2AuthorizationCodeFlow)
853 } deriving (Eq, Show, Generic, Data, Typeable)
841 } deriving (Eq, Show, Generic, Data, Typeable)
854
842
843+type BearerFormat = Text
844+
845+data HttpSchemeType
846+ = HttpSchemeBearer (Maybe BearerFormat)
847+ | HttpSchemeBasic
848+ | HttpSchemeCustom Text
849+ deriving (Eq, Show, Generic, Data, Typeable)
850+
851+-- |
852+--
853+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer Nothing))
854+-- {
855+-- "scheme": "bearer",
856+-- "type": "http"
857+-- }
858+--
859+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer (Just "jwt")))
860+-- {
861+-- "bearerFormat": "jwt",
862+-- "scheme": "bearer",
863+-- "type": "http"
864+-- }
865+--
866+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp HttpSchemeBasic)
867+-- {
868+-- "scheme": "basic",
869+-- "type": "http"
870+-- }
871+--
872+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeCustom "CANARY"))
873+-- {
874+-- "scheme": "CANARY",
875+-- "type": "http"
876+-- }
877+--
878+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeApiKey (ApiKeyParams "id" ApiKeyCookie))
879+-- {
880+-- "in": "cookie",
881+-- "name": "id",
882+-- "type": "apiKey"
883+-- }
884+--
855data SecuritySchemeType
885data SecuritySchemeType
856− = SecuritySchemeHttp
886+ = SecuritySchemeHttp HttpSchemeType
857 | SecuritySchemeApiKey ApiKeyParams
887 | SecuritySchemeApiKey ApiKeyParams
858 | SecuritySchemeOAuth2 OAuth2Flows
888 | SecuritySchemeOAuth2 OAuth2Flows
859 | SecuritySchemeOpenIdConnect URL
889 | SecuritySchemeOpenIdConnect URL
@@ -867,6 +897,10 @@ data SecurityScheme = SecurityScheme
867 , _securitySchemeDescription :: Maybe Text
897 , _securitySchemeDescription :: Maybe Text
868 } deriving (Eq, Show, Generic, Data, Typeable)
898 } deriving (Eq, Show, Generic, Data, Typeable)
869
899
900+newtype SecurityDefinitions
901+ = SecurityDefinitions (Definitions SecurityScheme)
902+ deriving (Eq, Show, Generic, Data, Typeable)
903+
870-- | Lists the required security schemes to execute this operation.
904-- | Lists the required security schemes to execute this operation.
871-- The object can have multiple security schemes declared in it which are all required
905-- The object can have multiple security schemes declared in it which are all required
872-- (that is, there is a logical AND between the schemes).
906-- (that is, there is a logical AND between the schemes).
@@ -884,7 +918,7 @@ data Tag = Tag
884 _tagName :: TagName
918 _tagName :: TagName
885
919
886 -- | A short description for the tag.
920 -- | A short description for the tag.
887− -- GFM syntax can be used for rich text representation.
921+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
888 , _tagDescription :: Maybe Text
922 , _tagDescription :: Maybe Text
889
923
890 -- | Additional external documentation for this tag.
924 -- | Additional external documentation for this tag.
@@ -899,7 +933,7 @@ instance IsString Tag where
899-- | Allows referencing an external resource for extended documentation.
933-- | Allows referencing an external resource for extended documentation.
900data ExternalDocs = ExternalDocs
934data ExternalDocs = ExternalDocs
901 { -- | A short description of the target documentation.
935 { -- | A short description of the target documentation.
902− -- GFM syntax can be used for rich text representation.
936+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
903 _externalDocsDescription :: Maybe Text
937 _externalDocsDescription :: Maybe Text
904
938
905 -- | The URL for the target documentation.
939 -- | The URL for the target documentation.
@@ -932,6 +966,7 @@ data AdditionalProperties
932-- Generic instances
966-- Generic instances
933-------------------------------------------------------------------------------
967-------------------------------------------------------------------------------
934
968
969+deriveGeneric ''Server
935deriveGeneric ''Components
970deriveGeneric ''Components
936deriveGeneric ''Header
971deriveGeneric ''Header
937deriveGeneric ''OAuth2Flow
972deriveGeneric ''OAuth2Flow
@@ -945,18 +980,18 @@ deriveGeneric ''MediaTypeObject
945deriveGeneric ''Responses
980deriveGeneric ''Responses
946deriveGeneric ''SecurityScheme
981deriveGeneric ''SecurityScheme
947deriveGeneric ''Schema
982deriveGeneric ''Schema
948−deriveGeneric ''ParamSchema
983+deriveGeneric ''OpenApi
949−deriveGeneric ''Swagger
950deriveGeneric ''Example
984deriveGeneric ''Example
951deriveGeneric ''Encoding
985deriveGeneric ''Encoding
986+deriveGeneric ''Link
952
987
953-- =======================================================================
988-- =======================================================================
954-- Monoid instances
989-- Monoid instances
955-- =======================================================================
990-- =======================================================================
956
991
957−instance Semigroup Swagger where
992+instance Semigroup OpenApi where
958 (<>) = genericMappend
993 (<>) = genericMappend
959−instance Monoid Swagger where
994+instance Monoid OpenApi where
960 mempty = genericMempty
995 mempty = genericMempty
961 mappend = (<>)
996 mappend = (<>)
962
997
@@ -990,12 +1025,6 @@ instance Monoid Schema where
990 mempty = genericMempty
1025 mempty = genericMempty
991 mappend = (<>)
1026 mappend = (<>)
992
1027
993−instance Semigroup (ParamSchema t) where
994− (<>) = genericMappend
995−instance Monoid (ParamSchema t) where
996− mempty = genericMempty
997− mappend = (<>)
998−
999instance Semigroup Param where
1028instance Semigroup Param where
1000 (<>) = genericMappend
1029 (<>) = genericMappend
1001instance Monoid Param where
1030instance Monoid Param where
@@ -1044,12 +1073,38 @@ instance Monoid Operation where
1044 mempty = genericMempty
1073 mempty = genericMempty
1045 mappend = (<>)
1074 mappend = (<>)
1046
1075
1076+instance Semigroup (OAuth2Flow p) where
1077+ l@OAuth2Flow{ _oAath2RefreshUrl = lUrl, _oAuth2Scopes = lScopes }
1078+ <> OAuth2Flow { _oAath2RefreshUrl = rUrl, _oAuth2Scopes = rScopes } =
1079+ l { _oAath2RefreshUrl = swaggerMappend lUrl rUrl, _oAuth2Scopes = lScopes <> rScopes }
1080+
1081+-- swaggerMappend has First-like semantics, and here we need mappend'ing under Maybes.
1047instance Semigroup OAuth2Flows where
1082instance Semigroup OAuth2Flows where
1048− (<>) = genericMappend
1083+ l <> r = OAuth2Flows
1084+ { _oAuth2FlowsImplicit = _oAuth2FlowsImplicit l <> _oAuth2FlowsImplicit r
1085+ , _oAuth2FlowsPassword = _oAuth2FlowsPassword l <> _oAuth2FlowsPassword r
1086+ , _oAuth2FlowsClientCredentials = _oAuth2FlowsClientCredentials l <> _oAuth2FlowsClientCredentials r
1087+ , _oAuth2FlowsAuthorizationCode = _oAuth2FlowsAuthorizationCode l <> _oAuth2FlowsAuthorizationCode r
1088+ }
1089+
1049instance Monoid OAuth2Flows where
1090instance Monoid OAuth2Flows where
1050 mempty = genericMempty
1091 mempty = genericMempty
1051 mappend = (<>)
1092 mappend = (<>)
1052
1093
1094+instance Semigroup SecurityScheme where
1095+ SecurityScheme (SecuritySchemeOAuth2 lFlows) lDesc
1096+ <> SecurityScheme (SecuritySchemeOAuth2 rFlows) rDesc =
1097+ SecurityScheme (SecuritySchemeOAuth2 $ lFlows <> rFlows) (swaggerMappend lDesc rDesc)
1098+ l <> _ = l
1099+
1100+instance Semigroup SecurityDefinitions where
1101+ (SecurityDefinitions sd1) <> (SecurityDefinitions sd2) =
1102+ SecurityDefinitions $ InsOrdHashMap.unionWith (<>) sd1 sd2
1103+
1104+instance Monoid SecurityDefinitions where
1105+ mempty = SecurityDefinitions InsOrdHashMap.empty
1106+ mappend = (<>)
1107+
1053instance Semigroup RequestBody where
1108instance Semigroup RequestBody where
1054 (<>) = genericMappend
1109 (<>) = genericMappend
1055instance Monoid RequestBody where
1110instance Monoid RequestBody where
@@ -1064,19 +1119,19 @@ instance SwaggerMonoid Info
1064instance SwaggerMonoid Components
1119instance SwaggerMonoid Components
1065instance SwaggerMonoid PathItem
1120instance SwaggerMonoid PathItem
1066instance SwaggerMonoid Schema
1121instance SwaggerMonoid Schema
1067−instance SwaggerMonoid (ParamSchema t)
1068instance SwaggerMonoid Param
1122instance SwaggerMonoid Param
1069instance SwaggerMonoid Responses
1123instance SwaggerMonoid Responses
1070instance SwaggerMonoid Response
1124instance SwaggerMonoid Response
1071instance SwaggerMonoid ExternalDocs
1125instance SwaggerMonoid ExternalDocs
1072instance SwaggerMonoid Operation
1126instance SwaggerMonoid Operation
1073instance (Eq a, Hashable a) => SwaggerMonoid (InsOrdHashSet a)
1127instance (Eq a, Hashable a) => SwaggerMonoid (InsOrdHashSet a)
1128+instance SwaggerMonoid SecurityDefinitions
1074
1129
1075instance SwaggerMonoid MimeList
1130instance SwaggerMonoid MimeList
1076deriving instance SwaggerMonoid URL
1131deriving instance SwaggerMonoid URL
1077
1132
1078−instance SwaggerMonoid (SwaggerType t) where
1133+instance SwaggerMonoid OpenApiType where
1079− swaggerMempty = SwaggerString
1134+ swaggerMempty = OpenApiString
1080 swaggerMappend _ y = y
1135 swaggerMappend _ y = y
1081
1136
1082instance SwaggerMonoid ParamLocation where
1137instance SwaggerMonoid ParamLocation where
@@ -1096,6 +1151,12 @@ instance Monoid a => SwaggerMonoid (Referenced a) where
1096-- Simple Generic-based ToJSON instances
1151-- Simple Generic-based ToJSON instances
1097-- =======================================================================
1152-- =======================================================================
1098
1153
1154+instance ToJSON Style where
1155+ toJSON = genericToJSON (jsonPrefix "Style")
1156+
1157+instance ToJSON OpenApiType where
1158+ toJSON = genericToJSON (jsonPrefix "Swagger")
1159+
1099instance ToJSON ParamLocation where
1160instance ToJSON ParamLocation where
1100 toJSON = genericToJSON (jsonPrefix "Param")
1161 toJSON = genericToJSON (jsonPrefix "Param")
1101
1162
@@ -1108,9 +1169,6 @@ instance ToJSON Contact where
1108instance ToJSON License where
1169instance ToJSON License where
1109 toJSON = genericToJSON (jsonPrefix "License")
1170 toJSON = genericToJSON (jsonPrefix "License")
1110
1171
1111−instance ToJSON Server where
1112− toJSON = genericToJSON (jsonPrefix "Server")
1113−
1114instance ToJSON ServerVariable where
1172instance ToJSON ServerVariable where
1115 toJSON = genericToJSON (jsonPrefix "ServerVariable")
1173 toJSON = genericToJSON (jsonPrefix "ServerVariable")
1116
1174
@@ -1148,6 +1206,12 @@ instance ToJSON OAuth2AuthorizationCodeFlow where
1148-- Simple Generic-based FromJSON instances
1206-- Simple Generic-based FromJSON instances
1149-- =======================================================================
1207-- =======================================================================
1150
1208
1209+instance FromJSON Style where
1210+ parseJSON = genericParseJSON (jsonPrefix "Style")
1211+
1212+instance FromJSON OpenApiType where
1213+ parseJSON = genericParseJSON (jsonPrefix "Swagger")
1214+
1151instance FromJSON ParamLocation where
1215instance FromJSON ParamLocation where
1152 parseJSON = genericParseJSON (jsonPrefix "Param")
1216 parseJSON = genericParseJSON (jsonPrefix "Param")
1153
1217
@@ -1160,9 +1224,6 @@ instance FromJSON Contact where
1160instance FromJSON License where
1224instance FromJSON License where
1161 parseJSON = genericParseJSON (jsonPrefix "License")
1225 parseJSON = genericParseJSON (jsonPrefix "License")
1162
1226
1163−instance FromJSON Server where
1164− parseJSON = genericParseJSON (jsonPrefix "Server")
1165−
1166instance FromJSON ServerVariable where
1227instance FromJSON ServerVariable where
1167 parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
1228 parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
1168
1229
@@ -1197,7 +1258,14 @@ instance FromJSON OAuth2AuthorizationCodeFlow where
1197-- Manual ToJSON instances
1258-- Manual ToJSON instances
1198-- =======================================================================
1259-- =======================================================================
1199
1260
1261+instance ToJSON MediaType where
1262+ toJSON = toJSON . show
1263+ toEncoding = toEncoding . show
1264+
1265+instance ToJSONKey MediaType where
1266+ toJSONKey = JSON.toJSONKeyText (Text.pack . show)
1267+
1200−instance (Eq p, ToJSON p, AesonDefaultValue p) =>ToJSON (OAuth2Flow p) where
1268+instance (Eq p, ToJSON p, AesonDefaultValue p) => ToJSON (OAuth2Flow p) where
1201 toJSON = sopSwaggerGenericToJSON
1269 toJSON = sopSwaggerGenericToJSON
1202 toEncoding = sopSwaggerGenericToEncoding
1270 toEncoding = sopSwaggerGenericToEncoding
1203
1271
@@ -1206,34 +1274,50 @@ instance ToJSON OAuth2Flows where
1206 toEncoding = sopSwaggerGenericToEncoding
1274 toEncoding = sopSwaggerGenericToEncoding
1207
1275
1208instance ToJSON SecuritySchemeType where
1276instance ToJSON SecuritySchemeType where
1209− toJSON SecuritySchemeHttp
1277+ toJSON (SecuritySchemeHttp ty) = case ty of
1278+ HttpSchemeBearer mFmt ->
1279+ object $ [ "type" .= ("http" :: Text)
1280+ , "scheme" .= ("bearer" :: Text)
1281+ ] <> maybe [] (\t -> ["bearerFormat" .= t]) mFmt
1282+ HttpSchemeBasic ->
1283+ object [ "type" .= ("http" :: Text)
1284+ , "scheme" .= ("basic" :: Text)
1285+ ]
1286+ HttpSchemeCustom t ->
1210− = object [ "type" .= ("http" :: Text) ]
1287+ object [ "type" .= ("http" :: Text)
1288+ , "scheme" .= t
1289+ ]
1211 toJSON (SecuritySchemeApiKey params)
1290 toJSON (SecuritySchemeApiKey params)
1212 = toJSON params
1291 = toJSON params
1213 <+> object [ "type" .= ("apiKey" :: Text) ]
1292 <+> object [ "type" .= ("apiKey" :: Text) ]
1214− toJSON (SecuritySchemeOAuth2 params)
1293+ toJSON (SecuritySchemeOAuth2 params) = object
1215− = toJSON params
1216− <+> object [ "type" .= ("oauth2" :: Text) ]
1294+ [ "type" .= ("oauth2" :: Text)
1295+ , "flows" .= toJSON params
1296+ ]
1217 toJSON (SecuritySchemeOpenIdConnect url) = object
1297 toJSON (SecuritySchemeOpenIdConnect url) = object
1218 [ "type" .= ("openIdConnect" :: Text)
1298 [ "type" .= ("openIdConnect" :: Text)
1219 , "openIdConnectUrl" .= url
1299 , "openIdConnectUrl" .= url
1220 ]
1300 ]
1221
1301
1222−instance ToJSON Swagger where
1302+instance ToJSON OpenApi where
1223 toJSON a = sopSwaggerGenericToJSON a &
1303 toJSON a = sopSwaggerGenericToJSON a &
1224− if InsOrdHashMap.null (_swaggerPaths a)
1304+ if InsOrdHashMap.null (_openApiPaths a)
1225 then (<+> object ["paths" .= object []])
1305 then (<+> object ["paths" .= object []])
1226 else id
1306 else id
1227 toEncoding = sopSwaggerGenericToEncoding
1307 toEncoding = sopSwaggerGenericToEncoding
1228
1308
1229−instance ToJSON SecurityScheme where
1309+instance ToJSON Server where
1230 toJSON = sopSwaggerGenericToJSON
1310 toJSON = sopSwaggerGenericToJSON
1231 toEncoding = sopSwaggerGenericToEncoding
1311 toEncoding = sopSwaggerGenericToEncoding
1232
1312
1233−instance ToJSON Schema where
1313+instance ToJSON SecurityScheme where
1234 toJSON = sopSwaggerGenericToJSON
1314 toJSON = sopSwaggerGenericToJSON
1235 toEncoding = sopSwaggerGenericToEncoding
1315 toEncoding = sopSwaggerGenericToEncoding
1236
1316
1317+instance ToJSON Schema where
1318+ toJSON = sopSwaggerGenericToJSONWithOpts $
1319+ mkSwaggerAesonOptions "schema" & saoSubObject ?~ "items"
1320+
1237instance ToJSON Header where
1321instance ToJSON Header where
1238 toJSON = sopSwaggerGenericToJSON
1322 toJSON = sopSwaggerGenericToJSON
1239 toEncoding = sopSwaggerGenericToEncoding
1323 toEncoding = sopSwaggerGenericToEncoding
@@ -1241,20 +1325,21 @@ instance ToJSON Header where
1241-- | As for nullary schema for 0-arity type constructors, see
1325-- | As for nullary schema for 0-arity type constructors, see
1242-- <https://github.com/GetShopTV/swagger2/issues/167>.
1326-- <https://github.com/GetShopTV/swagger2/issues/167>.
1243--
1327--
1244−-- >>> encode (SwaggerItemsArray [])
1328+-- >>> BSL.putStrLn $ encodePretty (OpenApiItemsArray [])
1329+-- {
1330+-- "example": [],
1331+-- "items": {},
1245−-- "{\"example\":[],\"items\":{},\"maxItems\":0}"
1332+-- "maxItems": 0
1333+-- }
1246--
1334--
1247−instance ToJSON (ParamSchema t) => ToJSON (SwaggerItems t) where
1335+instance ToJSON OpenApiItems where
1248− toJSON (SwaggerItemsPrimitive fmt schema) = object
1336+ toJSON (OpenApiItemsObject x) = object [ "items" .= x ]
1249− [ "collectionFormat" .= fmt
1250− , "items" .= schema ]
1251− toJSON (SwaggerItemsObject x) = object [ "items" .= x ]
1252− toJSON (SwaggerItemsArray []) = object
1337+ toJSON (OpenApiItemsArray []) = object
1253 [ "items" .= object []
1338 [ "items" .= object []
1254 , "maxItems" .= (0 :: Int)
1339 , "maxItems" .= (0 :: Int)
1255 , "example" .= Array mempty
1340 , "example" .= Array mempty
1256 ]
1341 ]
1257− toJSON (SwaggerItemsArray x) = object [ "items" .= x ]
1342+ toJSON (OpenApiItemsArray x) = object [ "items" .= x ]
1258
1343
1259instance ToJSON Components where
1344instance ToJSON Components where
1260 toJSON = sopSwaggerGenericToJSON
1345 toJSON = sopSwaggerGenericToJSON
@@ -1299,6 +1384,13 @@ instance ToJSON Encoding where
1299 toJSON = sopSwaggerGenericToJSON
1384 toJSON = sopSwaggerGenericToJSON
1300 toEncoding = sopSwaggerGenericToEncoding
1385 toEncoding = sopSwaggerGenericToEncoding
1301
1386
1387+instance ToJSON Link where
1388+ toJSON = sopSwaggerGenericToJSON
1389+ toEncoding = sopSwaggerGenericToEncoding
1390+
1391+instance ToJSON SecurityDefinitions where
1392+ toJSON (SecurityDefinitions sd) = toJSON sd
1393+
1302instance ToJSON Reference where
1394instance ToJSON Reference where
1303 toJSON (Reference ref) = object [ "$ref" .= ref ]
1395 toJSON (Reference ref) = object [ "$ref" .= ref ]
1304
1396
@@ -1312,37 +1404,31 @@ instance ToJSON (Referenced Response) where toJSON = referencedToJSON "#/compone
1312instance ToJSON (Referenced RequestBody) where toJSON = referencedToJSON "#/components/requestBodies/"
1404instance ToJSON (Referenced RequestBody) where toJSON = referencedToJSON "#/components/requestBodies/"
1313instance ToJSON (Referenced Example) where toJSON = referencedToJSON "#/components/examples/"
1405instance ToJSON (Referenced Example) where toJSON = referencedToJSON "#/components/examples/"
1314instance ToJSON (Referenced Header) where toJSON = referencedToJSON "#/components/headers/"
1406instance ToJSON (Referenced Header) where toJSON = referencedToJSON "#/components/headers/"
1315−
1407+instance ToJSON (Referenced Link) where toJSON = referencedToJSON "#/components/links/"
1316−instance ToJSON (SwaggerType t) where
1408+instance ToJSON (Referenced Callback) where toJSON = referencedToJSON "#/components/callbacks/"
1317− toJSON SwaggerArray = "array"
1318− toJSON SwaggerString = "string"
1319− toJSON SwaggerInteger = "integer"
1320− toJSON SwaggerNumber = "number"
1321− toJSON SwaggerBoolean = "boolean"
1322− toJSON SwaggerFile = "file"
1323− toJSON SwaggerNull = "null"
1324− toJSON SwaggerObject = "object"
1325−
1326−instance ToJSON (CollectionFormat t) where
1327− toJSON CollectionCSV = "csv"
1328− toJSON CollectionSSV = "ssv"
1329− toJSON CollectionTSV = "tsv"
1330− toJSON CollectionPipes = "pipes"
1331− toJSON CollectionMulti = "multi"
1332−
1333−instance ToJSON (ParamSchema k) where
1334− -- TODO: this is a bit fishy, why we need sub object only in `ToJSON`?
1335− toJSON = sopSwaggerGenericToJSONWithOpts $
1336− mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1337
1409
1338instance ToJSON AdditionalProperties where
1410instance ToJSON AdditionalProperties where
1339 toJSON (AdditionalPropertiesAllowed b) = toJSON b
1411 toJSON (AdditionalPropertiesAllowed b) = toJSON b
1340 toJSON (AdditionalPropertiesSchema s) = toJSON s
1412 toJSON (AdditionalPropertiesSchema s) = toJSON s
1341
1413
1414+instance ToJSON ExpressionOrValue where
1415+ toJSON (Expression expr) = toJSON expr
1416+ toJSON (Value val) = toJSON val
1417+
1418+instance ToJSON Callback where
1419+ toJSON (Callback ps) = toJSON ps
1420+
1342-- =======================================================================
1421-- =======================================================================
1343-- Manual FromJSON instances
1422-- Manual FromJSON instances
1344-- =======================================================================
1423-- =======================================================================
1345
1424
1425+instance FromJSON MediaType where
1426+ parseJSON = withText "MediaType" $ \str ->
1427+ maybe (fail $ "Invalid media type literal " <> Text.unpack str) pure $ parseAccept $ encodeUtf8 str
1428+
1429+instance FromJSONKey MediaType where
1430+ fromJSONKey = FromJSONKeyTextParser (parseJSON . String)
1431+
1346instance (Eq p, FromJSON p, AesonDefaultValue p) => FromJSON (OAuth2Flow p) where
1432instance (Eq p, FromJSON p, AesonDefaultValue p) => FromJSON (OAuth2Flow p) where
1347 parseJSON = sopSwaggerGenericParseJSON
1433 parseJSON = sopSwaggerGenericParseJSON
1348
1434
@@ -1353,14 +1439,22 @@ instance FromJSON SecuritySchemeType where
1353 parseJSON js@(Object o) = do
1439 parseJSON js@(Object o) = do
1354 (t :: Text) <- o .: "type"
1440 (t :: Text) <- o .: "type"
1355 case t of
1441 case t of
1442+ "http" -> do
1443+ scheme <- o .: "scheme"
1356− "http" -> pure SecuritySchemeHttp
1444+ SecuritySchemeHttp <$> case scheme of
1445+ "bearer" -> HttpSchemeBearer <$> (o .:! "bearerFormat")
1446+ "basic" -> pure HttpSchemeBasic
1447+ t -> pure $ HttpSchemeCustom t
1357 "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1448 "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1358− "oauth2" -> SecuritySchemeOAuth2 <$> parseJSON js
1449+ "oauth2" -> SecuritySchemeOAuth2 <$> (o .: "flows")
1359 "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
1450 "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
1360 _ -> empty
1451 _ -> empty
1361 parseJSON _ = empty
1452 parseJSON _ = empty
1362
1453
1363−instance FromJSON Swagger where
1454+instance FromJSON OpenApi where
1455+ parseJSON = sopSwaggerGenericParseJSON
1456+
1457+instance FromJSON Server where
1364 parseJSON = sopSwaggerGenericParseJSON
1458 parseJSON = sopSwaggerGenericParseJSON
1365
1459
1366instance FromJSON SecurityScheme where
1460instance FromJSON SecurityScheme where
@@ -1369,48 +1463,28 @@ instance FromJSON SecurityScheme where
1369instance FromJSON Schema where
1463instance FromJSON Schema where
1370 parseJSON = fmap nullaryCleanup . sopSwaggerGenericParseJSON
1464 parseJSON = fmap nullaryCleanup . sopSwaggerGenericParseJSON
1371 where nullaryCleanup :: Schema -> Schema
1465 where nullaryCleanup :: Schema -> Schema
1372− nullaryCleanup s@Schema{_schemaParamSchema=ps} =
1466+ nullaryCleanup s =
1373− if _paramSchemaItems ps == Just (SwaggerItemsArray [])
1467+ if _schemaItems s == Just (OpenApiItemsArray [])
1374 then s { _schemaExample = Nothing
1468 then s { _schemaExample = Nothing
1375− , _schemaParamSchema = ps { _paramSchemaMaxItems = Nothing } }
1469+ , _schemaMaxItems = Nothing
1470+ }
1376 else s
1471 else s
1377
1472
1378instance FromJSON Header where
1473instance FromJSON Header where
1379 parseJSON = sopSwaggerGenericParseJSON
1474 parseJSON = sopSwaggerGenericParseJSON
1380
1475
1381−instance (FromJSON (CollectionFormat ('SwaggerKindNormal t)), FromJSON (ParamSchema ('SwaggerKindNormal t))) => FromJSON (SwaggerItems ('SwaggerKindNormal t)) where
1476+instance FromJSON OpenApiItems where
1382− parseJSON = withObject "SwaggerItemsPrimitive" $ \o -> SwaggerItemsPrimitive
1383− <$> o .:? "collectionFormat"
1384− <*> (o .: "items" >>= parseJSON)
1385−
1386−instance FromJSON (SwaggerItems 'SwaggerKindParamOtherSchema) where
1387− parseJSON = withObject "SwaggerItemsPrimitive" $ \o -> SwaggerItemsPrimitive
1388− <$> o .:? "collectionFormat"
1389− <*> ((o .: "items" >>= parseJSON) <|> fail ("foo" ++ show o))
1390−
1391−-- |
1392−--
1393−-- >>> decode "{}" :: Maybe (SwaggerItems 'SwaggerKindSchema)
1394−-- Just (SwaggerItemsArray [])
1395−--
1396−-- >>> eitherDecode "{\"$ref\":\"#/definitions/example\"}" :: Either String (SwaggerItems 'SwaggerKindSchema)
1397−-- Right (SwaggerItemsObject (Ref (Reference {getReference = "example"})))
1398−--
1399−-- >>> eitherDecode "[{\"$ref\":\"#/definitions/example\"}]" :: Either String (SwaggerItems 'SwaggerKindSchema)
1400−-- Right (SwaggerItemsArray [Ref (Reference {getReference = "example"})])
1401−--
1402−instance FromJSON (SwaggerItems 'SwaggerKindSchema) where
1403 parseJSON js@(Object obj)
1477 parseJSON js@(Object obj)
1404− | null obj = pure $ SwaggerItemsArray [] -- Nullary schema.
1478+ | null obj = pure $ OpenApiItemsArray [] -- Nullary schema.
1405− | otherwise = SwaggerItemsObject <$> parseJSON js
1479+ | otherwise = OpenApiItemsObject <$> parseJSON js
1406− parseJSON js@(Array _) = SwaggerItemsArray <$> parseJSON js
1480+ parseJSON js@(Array _) = OpenApiItemsArray <$> parseJSON js
1407 parseJSON _ = empty
1481 parseJSON _ = empty
1408
1482
1409instance FromJSON Components where
1483instance FromJSON Components where
1410 parseJSON = sopSwaggerGenericParseJSON
1484 parseJSON = sopSwaggerGenericParseJSON
1411
1485
1412instance FromJSON MimeList where
1486instance FromJSON MimeList where
1413− parseJSON js = (MimeList . map fromString) <$> parseJSON js
1487+ parseJSON js = MimeList . map fromString <$> parseJSON js
1414
1488
1415instance FromJSON Param where
1489instance FromJSON Param where
1416 parseJSON = sopSwaggerGenericParseJSON
1490 parseJSON = sopSwaggerGenericParseJSON
@@ -1418,7 +1492,7 @@ instance FromJSON Param where
1418instance FromJSON Responses where
1492instance FromJSON Responses where
1419 parseJSON (Object o) = Responses
1493 parseJSON (Object o) = Responses
1420 <$> o .:? "default"
1494 <$> o .:? "default"
1421− <*> (parseJSON (Object (HashMap.delete "default" o)))
1495+ <*> parseJSON (Object (deleteKey "default" o))
1422 parseJSON _ = empty
1496 parseJSON _ = empty
1423
1497
1424instance FromJSON Example where
1498instance FromJSON Example where
@@ -1433,6 +1507,9 @@ instance FromJSON Operation where
1433instance FromJSON PathItem where
1507instance FromJSON PathItem where
1434 parseJSON = sopSwaggerGenericParseJSON
1508 parseJSON = sopSwaggerGenericParseJSON
1435
1509
1510+instance FromJSON SecurityDefinitions where
1511+ parseJSON js = SecurityDefinitions <$> parseJSON js
1512+
1436instance FromJSON RequestBody where
1513instance FromJSON RequestBody where
1437 parseJSON = sopSwaggerGenericParseJSON
1514 parseJSON = sopSwaggerGenericParseJSON
1438
1515
@@ -1442,6 +1519,9 @@ instance FromJSON MediaTypeObject where
1442instance FromJSON Encoding where
1519instance FromJSON Encoding where
1443 parseJSON = sopSwaggerGenericParseJSON
1520 parseJSON = sopSwaggerGenericParseJSON
1444
1521
1522+instance FromJSON Link where
1523+ parseJSON = sopSwaggerGenericParseJSON
1524+
1445instance FromJSON Reference where
1525instance FromJSON Reference where
1446 parseJSON (Object o) = Reference <$> o .: "$ref"
1526 parseJSON (Object o) = Reference <$> o .: "$ref"
1447 parseJSON _ = empty
1527 parseJSON _ = empty
@@ -1465,44 +1545,30 @@ instance FromJSON (Referenced Response) where parseJSON = referencedParseJSON "#
1465instance FromJSON (Referenced RequestBody) where parseJSON = referencedParseJSON "#/components/requestBodies/"
1545instance FromJSON (Referenced RequestBody) where parseJSON = referencedParseJSON "#/components/requestBodies/"
1466instance FromJSON (Referenced Example) where parseJSON = referencedParseJSON "#/components/examples/"
1546instance FromJSON (Referenced Example) where parseJSON = referencedParseJSON "#/components/examples/"
1467instance FromJSON (Referenced Header) where parseJSON = referencedParseJSON "#/components/headers/"
1547instance FromJSON (Referenced Header) where parseJSON = referencedParseJSON "#/components/headers/"
1548+instance FromJSON (Referenced Link) where parseJSON = referencedParseJSON "#/components/links/"
1549+instance FromJSON (Referenced Callback) where parseJSON = referencedParseJSON "#/components/callbacks/"
1468
1550
1469instance FromJSON Xml where
1551instance FromJSON Xml where
1470 parseJSON = genericParseJSON (jsonPrefix "xml")
1552 parseJSON = genericParseJSON (jsonPrefix "xml")
1471
1553
1472−instance FromJSON (SwaggerType 'SwaggerKindSchema) where
1473− parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray, SwaggerNull, SwaggerObject]
1474−
1475−instance FromJSON (SwaggerType 'SwaggerKindParamOtherSchema) where
1476− parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray, SwaggerFile]
1477−
1478−instance FromJSON (SwaggerType ('SwaggerKindNormal t)) where
1479− parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray]
1480−
1481−instance FromJSON (CollectionFormat ('SwaggerKindNormal t)) where
1482− parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes]
1483−
1484−-- NOTE: There aren't collections of 'Schema'
1485−--instance FromJSON (CollectionFormat (SwaggerKindSchema)) where
1486−-- parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes]
1487−
1488−instance FromJSON (CollectionFormat 'SwaggerKindParamOtherSchema) where
1489− parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes, CollectionMulti]
1490−
1491−instance (FromJSON (SwaggerType ('SwaggerKindNormal t)), FromJSON (SwaggerItems ('SwaggerKindNormal t))) => FromJSON (ParamSchema ('SwaggerKindNormal t)) where
1492− parseJSON = sopSwaggerGenericParseJSON
1493−instance FromJSON (ParamSchema 'SwaggerKindParamOtherSchema) where
1494− parseJSON = sopSwaggerGenericParseJSON
1495−instance FromJSON (ParamSchema 'SwaggerKindSchema) where
1496− parseJSON = sopSwaggerGenericParseJSON
1497−
1498instance FromJSON AdditionalProperties where
1554instance FromJSON AdditionalProperties where
1499 parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
1555 parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
1500 parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
1556 parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
1501
1557
1558+-- | All strings are parsed as expressions
1559+instance FromJSON ExpressionOrValue where
1560+ parseJSON (String expr) = pure $ Expression expr
1561+ parseJSON v = pure $ Value v
1562+
1563+instance FromJSON Callback where
1564+ parseJSON = fmap Callback . parseJSON
1565+
1566+instance HasSwaggerAesonOptions Server where
1567+ swaggerAesonOptions _ = mkSwaggerAesonOptions "server"
1502instance HasSwaggerAesonOptions Components where
1568instance HasSwaggerAesonOptions Components where
1503 swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
1569 swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
1504instance HasSwaggerAesonOptions Header where
1570instance HasSwaggerAesonOptions Header where
1505− swaggerAesonOptions _ = mkSwaggerAesonOptions "header" & saoSubObject ?~ "paramSchema"
1571+ swaggerAesonOptions _ = mkSwaggerAesonOptions "header"
1506instance AesonDefaultValue p => HasSwaggerAesonOptions (OAuth2Flow p) where
1572instance AesonDefaultValue p => HasSwaggerAesonOptions (OAuth2Flow p) where
1507 swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "params"
1573 swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "params"
1508instance HasSwaggerAesonOptions OAuth2Flows where
1574instance HasSwaggerAesonOptions OAuth2Flows where
@@ -1525,23 +1591,18 @@ instance HasSwaggerAesonOptions SecurityScheme where
1525 swaggerAesonOptions _ = mkSwaggerAesonOptions "securityScheme" & saoSubObject ?~ "type"
1591 swaggerAesonOptions _ = mkSwaggerAesonOptions "securityScheme" & saoSubObject ?~ "type"
1526instance HasSwaggerAesonOptions Schema where
1592instance HasSwaggerAesonOptions Schema where
1527 swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1593 swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1528−instance HasSwaggerAesonOptions Swagger where
1594+instance HasSwaggerAesonOptions OpenApi where
1529− swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.3")]
1595+ swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.0")]
1530instance HasSwaggerAesonOptions Example where
1596instance HasSwaggerAesonOptions Example where
1531 swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
1597 swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
1532instance HasSwaggerAesonOptions Encoding where
1598instance HasSwaggerAesonOptions Encoding where
1533 swaggerAesonOptions _ = mkSwaggerAesonOptions "encoding"
1599 swaggerAesonOptions _ = mkSwaggerAesonOptions "encoding"
1534
1600
1535−instance HasSwaggerAesonOptions (ParamSchema ('SwaggerKindNormal t)) where
1601+instance HasSwaggerAesonOptions Link where
1536− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1537−instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindParamOtherSchema) where
1538− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1539−-- NOTE: Schema doesn't have 'items' sub object!
1540−instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindSchema) where
1541− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema"
1602+ swaggerAesonOptions _ = mkSwaggerAesonOptions "link"
1542
1603
1604+instance AesonDefaultValue Server
1543instance AesonDefaultValue Components
1605instance AesonDefaultValue Components
1544−instance AesonDefaultValue (ParamSchema s)
1545instance AesonDefaultValue OAuth2ImplicitFlow
1606instance AesonDefaultValue OAuth2ImplicitFlow
1546instance AesonDefaultValue OAuth2PasswordFlow
1607instance AesonDefaultValue OAuth2PasswordFlow
1547instance AesonDefaultValue OAuth2ClientCredentialsFlow
1608instance AesonDefaultValue OAuth2ClientCredentialsFlow
@@ -1549,7 +1610,10 @@ instance AesonDefaultValue OAuth2AuthorizationCodeFlow
1549instance AesonDefaultValue p => AesonDefaultValue (OAuth2Flow p)
1610instance AesonDefaultValue p => AesonDefaultValue (OAuth2Flow p)
1550instance AesonDefaultValue Responses
1611instance AesonDefaultValue Responses
1551instance AesonDefaultValue SecuritySchemeType
1612instance AesonDefaultValue SecuritySchemeType
1552−instance AesonDefaultValue (SwaggerType a)
1613+instance AesonDefaultValue OpenApiType
1553instance AesonDefaultValue MimeList where defaultValue = Just mempty
1614instance AesonDefaultValue MimeList where defaultValue = Just mempty
1554instance AesonDefaultValue Info
1615instance AesonDefaultValue Info
1555instance AesonDefaultValue ParamLocation
1616instance AesonDefaultValue ParamLocation
1617+instance AesonDefaultValue Link
1618+instance AesonDefaultValue SecurityDefinitions where
1619+ defaultValue = Just mempty
src/Data/Swagger/Internal/AesonUtils.hs → src/Data/OpenApi/Internal/AesonUtils.hsrenamed+12−13
@@ -5,7 +5,7 @@
5{-# LANGUAGE ExplicitForAll #-}
5{-# LANGUAGE ExplicitForAll #-}
6{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TemplateHaskell #-}
7{-# LANGUAGE UndecidableSuperClasses #-}
7{-# LANGUAGE UndecidableSuperClasses #-}
8−module Data.Swagger.Internal.AesonUtils (
8+module Data.OpenApi.Internal.AesonUtils (
9 -- * Generic functions
9 -- * Generic functions
10 AesonDefaultValue(..),
10 AesonDefaultValue(..),
11 sopSwaggerGenericToJSON,
11 sopSwaggerGenericToJSON,
@@ -27,7 +27,7 @@ import Prelude.Compat
27import Control.Applicative ((<|>))
27import Control.Applicative ((<|>))
28import Control.Lens (makeLenses, (^.))
28import Control.Lens (makeLenses, (^.))
29import Control.Monad (unless)
29import Control.Monad (unless)
30−import Data.Aeson (ToJSON(..), FromJSON(..), Value(..), Object, object, (.:), (.:?), (.!=), withObject)
30+import Data.Aeson (ToJSON(..), FromJSON(..), Value(..), Object, object, (.:), (.:?), (.!=), withObject, Encoding, pairs, (.=), Series)
31import Data.Aeson.Types (Parser, Pair)
31import Data.Aeson.Types (Parser, Pair)
32import Data.Char (toLower, isUpper)
32import Data.Char (toLower, isUpper)
33import Data.Foldable (traverse_)
33import Data.Foldable (traverse_)
@@ -36,12 +36,11 @@ import Data.Text (Text)
36import Generics.SOP
36import Generics.SOP
37
37
38import qualified Data.Text as T
38import qualified Data.Text as T
39−import qualified Data.HashMap.Strict as HM
40import qualified Data.Set as Set
39import qualified Data.Set as Set
41import qualified Data.HashMap.Strict.InsOrd as InsOrd
40import qualified Data.HashMap.Strict.InsOrd as InsOrd
42import qualified Data.HashSet.InsOrd as InsOrdHS
41import qualified Data.HashSet.InsOrd as InsOrdHS
43
42
44−import Data.Aeson (Encoding, pairs, (.=), Series)
43+import Data.OpenApi.Aeson.Compat (keyToString, objectToList, stringToKey)
45
44
46-------------------------------------------------------------------------------
45-------------------------------------------------------------------------------
47-- SwaggerAesonOptions
46-- SwaggerAesonOptions
@@ -49,7 +48,7 @@ import Data.Aeson (Encoding, pairs, (.=), Series)
49
48
50data SwaggerAesonOptions = SwaggerAesonOptions
49data SwaggerAesonOptions = SwaggerAesonOptions
51 { _saoPrefix :: String
50 { _saoPrefix :: String
52− , _saoAdditionalPairs :: [(Text, Value)]
51+ , _saoAdditionalPairs :: [Pair]
53 , _saoSubObject :: Maybe String
52 , _saoSubObject :: Maybe String
54 }
53 }
55
54
@@ -156,14 +155,14 @@ sopSwaggerGenericToJSON'' (SwaggerAesonOptions prefix _ sub) = go
156 go Nil Nil Nil = []
155 go Nil Nil Nil = []
157 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
156 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
158 | Just name' == sub = case json of
157 | Just name' == sub = case json of
159− Object m -> HM.toList m ++ rest
158+ Object m -> objectToList m ++ rest
160 Null -> rest
159 Null -> rest
161 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show json
160 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show json
162 -- If default value: omit it.
161 -- If default value: omit it.
163 | Just x == def =
162 | Just x == def =
164 rest
163 rest
165 | otherwise =
164 | otherwise =
166− (T.pack name', json) : rest
165+ (stringToKey name', json) : rest
167 where
166 where
168 json = toJSON x
167 json = toJSON x
169 name' = fieldNameModifier name
168 name' = fieldNameModifier name
@@ -197,11 +196,11 @@ sopSwaggerGenericParseJSON = withObject "Swagger Record Object" $ \obj ->
197 proxy = Proxy :: Proxy a
196 proxy = Proxy :: Proxy a
198 opts = swaggerAesonOptions proxy
197 opts = swaggerAesonOptions proxy
199
198
200− parseAdditionalField :: Object -> (Text, Value) -> Parser ()
199+ parseAdditionalField :: Object -> Pair -> Parser ()
201 parseAdditionalField obj (k, v) = do
200 parseAdditionalField obj (k, v) = do
202 v' <- obj .: k
201 v' <- obj .: k
203 unless (v == v') $ fail $
202 unless (v == v') $ fail $
204− "Additonal field don't match for key " ++ T.unpack k
203+ "Additonal field don't match for key " ++ keyToString k
205 ++ ": " ++ show v
204 ++ ": " ++ show v
206 ++ " /= " ++ show v'
205 ++ " /= " ++ show v'
207
206
@@ -232,8 +231,8 @@ sopSwaggerGenericParseJSON'' (SwaggerAesonOptions prefix _ sub) obj = go
232 -- Note: we might strip fields of outer structure.
231 -- Note: we might strip fields of outer structure.
233 cons <$> (withDef $ parseJSON $ Object obj) <*> rest
232 cons <$> (withDef $ parseJSON $ Object obj) <*> rest
234 | otherwise = case def of
233 | otherwise = case def of
235− Just def' -> cons <$> obj .:? T.pack name' .!= def' <*> rest
234+ Just def' -> cons <$> obj .:? stringToKey name' .!= def' <*> rest
236− Nothing -> cons <$> obj .: T.pack name' <*> rest
235+ Nothing -> cons <$> obj .: stringToKey name' <*> rest
237 where
236 where
238 cons h t = I h :* t
237 cons h t = I h :* t
239 name' = fieldNameModifier name
238 name' = fieldNameModifier name
@@ -296,14 +295,14 @@ sopSwaggerGenericToEncoding'' (SwaggerAesonOptions prefix _ sub) = go
296 go Nil Nil Nil = mempty
295 go Nil Nil Nil = mempty
297 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
296 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
298 | Just name' == sub = case toJSON x of
297 | Just name' == sub = case toJSON x of
299− Object m -> pairsToSeries (HM.toList m) <> rest
298+ Object m -> pairsToSeries (objectToList m) <> rest
300 Null -> rest
299 Null -> rest
301 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show (toJSON x)
300 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show (toJSON x)
302 -- If default value: omit it.
301 -- If default value: omit it.
303 | Just x == def =
302 | Just x == def =
304 rest
303 rest
305 | otherwise =
304 | otherwise =
306− (T.pack name' .= x) <> rest
305+ (stringToKey name' .= x) <> rest
307 where
306 where
308 name' = fieldNameModifier name
307 name' = fieldNameModifier name
309 rest = go xs names defs
308 rest = go xs names defs
src/Data/Swagger/Internal/ParamSchema.hs → src/Data/OpenApi/Internal/ParamSchema.hsrenamed+77−53
@@ -15,7 +15,7 @@
15{-# OPTIONS_GHC -Wno-redundant-constraints #-}
15{-# OPTIONS_GHC -Wno-redundant-constraints #-}
16-- For TypeErrors
16-- For TypeErrors
17{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
17{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
18−module Data.Swagger.Internal.ParamSchema where
18+module Data.OpenApi.Internal.ParamSchema where
19
19
20import Control.Lens
20import Control.Lens
21import Data.Aeson (ToJSON (..))
21import Data.Aeson (ToJSON (..))
@@ -41,34 +41,40 @@ import Data.Word
41import Data.UUID.Types (UUID)
41import Data.UUID.Types (UUID)
42import Web.Cookie (SetCookie)
42import Web.Cookie (SetCookie)
43
43
44−import Data.Swagger.Internal
44+import Data.OpenApi.Internal
45−import Data.Swagger.Lens
45+import Data.OpenApi.Lens
46−import Data.Swagger.SchemaOptions
46+import Data.OpenApi.SchemaOptions
47
47
48import qualified Data.ByteString as BS
48import qualified Data.ByteString as BS
49−import qualified Data.ByteString.Lazy as BSL
49+import qualified Data.ByteString.Lazy.Char8 as BSL
50import GHC.TypeLits (TypeError, ErrorMessage(..))
50import GHC.TypeLits (TypeError, ErrorMessage(..))
51
51
52-- | Default schema for binary data (any sequence of octets).
52-- | Default schema for binary data (any sequence of octets).
53−binaryParamSchema :: ParamSchema t
53+binarySchema :: Schema
54−binaryParamSchema = mempty
54+binarySchema = mempty
55− & type_ ?~ SwaggerString
55+ & type_ ?~ OpenApiString
56 & format ?~ "binary"
56 & format ?~ "binary"
57
57
58-- | Default schema for binary data (base64 encoded).
58-- | Default schema for binary data (base64 encoded).
59−byteParamSchema :: ParamSchema t
59+byteSchema :: Schema
60−byteParamSchema = mempty
60+byteSchema = mempty
61− & type_ ?~ SwaggerString
61+ & type_ ?~ OpenApiString
62 & format ?~ "byte"
62 & format ?~ "byte"
63
63
64-- | Default schema for password string.
64-- | Default schema for password string.
65-- @"password"@ format is used to hint UIs the input needs to be obscured.
65-- @"password"@ format is used to hint UIs the input needs to be obscured.
66−passwordParamSchema :: ParamSchema t
66+passwordSchema :: Schema
67−passwordParamSchema = mempty
67+passwordSchema = mempty
68− & type_ ?~ SwaggerString
68+ & type_ ?~ OpenApiString
69 & format ?~ "password"
69 & format ?~ "password"
70
70
71−-- | Convert a type into a plain @'ParamSchema'@.
71+-- | Convert a type into a plain @'Schema'@.
72+--
73+-- In previous versions of the package there was a separate type called @ParamSchema@, which was
74+-- included in a greater 'Schema'. Now this is a single class, but distinction for schema generators
75+-- for "simple" types is preserved.
76+--
77+-- 'ToParamSchema' is suited only for primitive-like types without nested fields and such.
72--
78--
73-- An example type and instance:
79-- An example type and instance:
74--
80--
@@ -81,7 +87,7 @@ passwordParamSchema = mempty
81--
87--
82-- instance ToParamSchema Direction where
88-- instance ToParamSchema Direction where
83-- toParamSchema _ = mempty
89-- toParamSchema _ = mempty
84−-- & type_ ?~ SwaggerString
90+-- & type_ ?~ OpenApiString
85-- & enum_ ?~ [ \"Up\", \"Down\" ]
91-- & enum_ ?~ [ \"Up\", \"Down\" ]
86-- @
92-- @
87--
93--
@@ -106,24 +112,26 @@ passwordParamSchema = mempty
106class ToParamSchema a where
112class ToParamSchema a where
107 -- | Convert a type into a plain parameter schema.
113 -- | Convert a type into a plain parameter schema.
108 --
114 --
109− -- >>> encode $ toParamSchema (Proxy :: Proxy Integer)
115+ -- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy Integer)
116+ -- {
110− -- "{\"type\":\"integer\"}"
117+ -- "type": "integer"
118+ -- }
111− toParamSchema :: Proxy a -> ParamSchema t
119+ toParamSchema :: Proxy a -> Schema
112− default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> ParamSchema t
120+ default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> Schema
113 toParamSchema = genericToParamSchema defaultSchemaOptions
121 toParamSchema = genericToParamSchema defaultSchemaOptions
114
122
115instance {-# OVERLAPPING #-} ToParamSchema String where
123instance {-# OVERLAPPING #-} ToParamSchema String where
116− toParamSchema _ = mempty & type_ ?~ SwaggerString
124+ toParamSchema _ = mempty & type_ ?~ OpenApiString
117
125
118instance ToParamSchema Bool where
126instance ToParamSchema Bool where
119− toParamSchema _ = mempty & type_ ?~ SwaggerBoolean
127+ toParamSchema _ = mempty & type_ ?~ OpenApiBoolean
120
128
121instance ToParamSchema Integer where
129instance ToParamSchema Integer where
122− toParamSchema _ = mempty & type_ ?~ SwaggerInteger
130+ toParamSchema _ = mempty & type_ ?~ OpenApiInteger
123
131
124instance ToParamSchema Natural where
132instance ToParamSchema Natural where
125 toParamSchema _ = mempty
133 toParamSchema _ = mempty
126− & type_ ?~ SwaggerInteger
134+ & type_ ?~ OpenApiInteger
127 & minimum_ ?~ 0
135 & minimum_ ?~ 0
128 & exclusiveMinimum ?~ False
136 & exclusiveMinimum ?~ False
129
137
@@ -149,41 +157,45 @@ instance ToParamSchema Word64 where
149
157
150-- | Default plain schema for @'Bounded'@, @'Integral'@ types.
158-- | Default plain schema for @'Bounded'@, @'Integral'@ types.
151--
159--
152−-- >>> encode $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
160+-- >>> BSL.putStrLn $ encodePretty $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
161+-- {
162+-- "maximum": 127,
163+-- "minimum": -128,
153−-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
164+-- "type": "integer"
165+-- }
154−toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> ParamSchema t
166+toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> Schema
155toParamSchemaBoundedIntegral _ = mempty
167toParamSchemaBoundedIntegral _ = mempty
156− & type_ ?~ SwaggerInteger
168+ & type_ ?~ OpenApiInteger
157 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
169 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
158 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
170 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
159
171
160instance ToParamSchema Char where
172instance ToParamSchema Char where
161 toParamSchema _ = mempty
173 toParamSchema _ = mempty
162− & type_ ?~ SwaggerString
174+ & type_ ?~ OpenApiString
163 & maxLength ?~ 1
175 & maxLength ?~ 1
164 & minLength ?~ 1
176 & minLength ?~ 1
165
177
166instance ToParamSchema Scientific where
178instance ToParamSchema Scientific where
167− toParamSchema _ = mempty & type_ ?~ SwaggerNumber
179+ toParamSchema _ = mempty & type_ ?~ OpenApiNumber
168
180
169instance HasResolution a => ToParamSchema (Fixed a) where
181instance HasResolution a => ToParamSchema (Fixed a) where
170 toParamSchema _ = mempty
182 toParamSchema _ = mempty
171− & type_ ?~ SwaggerNumber
183+ & type_ ?~ OpenApiNumber
172 & multipleOf ?~ (recip . fromInteger $ resolution (Proxy :: Proxy a))
184 & multipleOf ?~ (recip . fromInteger $ resolution (Proxy :: Proxy a))
173
185
174instance ToParamSchema Double where
186instance ToParamSchema Double where
175 toParamSchema _ = mempty
187 toParamSchema _ = mempty
176− & type_ ?~ SwaggerNumber
188+ & type_ ?~ OpenApiNumber
177 & format ?~ "double"
189 & format ?~ "double"
178
190
179instance ToParamSchema Float where
191instance ToParamSchema Float where
180 toParamSchema _ = mempty
192 toParamSchema _ = mempty
181− & type_ ?~ SwaggerNumber
193+ & type_ ?~ OpenApiNumber
182 & format ?~ "float"
194 & format ?~ "float"
183
195
184−timeParamSchema :: String -> ParamSchema t
196+timeParamSchema :: String -> Schema
185timeParamSchema fmt = mempty
197timeParamSchema fmt = mempty
186− & type_ ?~ SwaggerString
198+ & type_ ?~ OpenApiString
187 & format ?~ T.pack fmt
199 & format ?~ T.pack fmt
188
200
189-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
201-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
@@ -204,9 +216,9 @@ instance ToParamSchema LocalTime where
204
216
205-- |
217-- |
206-- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. format
218-- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. format
207−-- Just "yyyy-mm-ddThh:MM:ss+hhMM"
219+-- Just "date-time"
208instance ToParamSchema ZonedTime where
220instance ToParamSchema ZonedTime where
209− toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss+hhMM"
221+ toParamSchema _ = timeParamSchema "date-time"
210
222
211-- |
223-- |
212-- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. format
224-- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. format
@@ -225,18 +237,18 @@ instance ToParamSchema TL.Text where
225
237
226instance ToParamSchema Version where
238instance ToParamSchema Version where
227 toParamSchema _ = mempty
239 toParamSchema _ = mempty
228− & type_ ?~ SwaggerString
240+ & type_ ?~ OpenApiString
229 & pattern ?~ "^\\d+(\\.\\d+)*$"
241 & pattern ?~ "^\\d+(\\.\\d+)*$"
230
242
231instance ToParamSchema SetCookie where
243instance ToParamSchema SetCookie where
232 toParamSchema _ = mempty
244 toParamSchema _ = mempty
233− & type_ ?~ SwaggerString
245+ & type_ ?~ OpenApiString
234
246
235type family ToParamSchemaByteStringError bs where
247type family ToParamSchemaByteStringError bs where
236 ToParamSchemaByteStringError bs = TypeError
248 ToParamSchemaByteStringError bs = TypeError
237 ( 'Text "Impossible to have an instance " :<>: ShowType (ToParamSchema bs) :<>: Text "."
249 ( 'Text "Impossible to have an instance " :<>: ShowType (ToParamSchema bs) :<>: Text "."
238 :$$: 'Text "Please, use a newtype wrapper around " :<>: ShowType bs :<>: Text " instead."
250 :$$: 'Text "Please, use a newtype wrapper around " :<>: ShowType bs :<>: Text " instead."
239− :$$: 'Text "Consider using byteParamSchema or binaryParamSchema templates." )
251+ :$$: 'Text "Consider using byteParamSchema or binaryParamSchemaemplates." )
240
252
241instance ToParamSchemaByteStringError BS.ByteString => ToParamSchema BS.ByteString where toParamSchema = error "impossible"
253instance ToParamSchemaByteStringError BS.ByteString => ToParamSchema BS.ByteString where toParamSchema = error "impossible"
242instance ToParamSchemaByteStringError BSL.ByteString => ToParamSchema BSL.ByteString where toParamSchema = error "impossible"
254instance ToParamSchemaByteStringError BSL.ByteString => ToParamSchema BSL.ByteString where toParamSchema = error "impossible"
@@ -253,8 +265,8 @@ instance ToParamSchema a => ToParamSchema (Identity a) where toParamSchema _ = t
253
265
254instance ToParamSchema a => ToParamSchema [a] where
266instance ToParamSchema a => ToParamSchema [a] where
255 toParamSchema _ = mempty
267 toParamSchema _ = mempty
256− & type_ ?~ SwaggerArray
268+ & type_ ?~ OpenApiArray
257− & items ?~ SwaggerItemsPrimitive Nothing (toParamSchema (Proxy :: Proxy a))
269+ & items ?~ OpenApiItemsObject (Inline $ toParamSchema (Proxy :: Proxy a))
258
270
259instance ToParamSchema a => ToParamSchema (V.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
271instance ToParamSchema a => ToParamSchema (V.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
260instance ToParamSchema a => ToParamSchema (VP.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
272instance ToParamSchema a => ToParamSchema (VP.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
@@ -269,29 +281,40 @@ instance ToParamSchema a => ToParamSchema (HashSet a) where
269 toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a))
281 toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a))
270
282
271-- |
283-- |
272−-- >>> encode $ toParamSchema (Proxy :: Proxy ())
284+-- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy ())
285+-- {
273−-- "{\"type\":\"string\",\"enum\":[\"_\"]}"
286+-- "enum": [
287+-- "_"
288+-- ],
289+-- "type": "string"
290+-- }
274instance ToParamSchema () where
291instance ToParamSchema () where
275 toParamSchema _ = mempty
292 toParamSchema _ = mempty
276− & type_ ?~ SwaggerString
293+ & type_ ?~ OpenApiString
277 & enum_ ?~ ["_"]
294 & enum_ ?~ ["_"]
278
295
279instance ToParamSchema UUID where
296instance ToParamSchema UUID where
280 toParamSchema _ = mempty
297 toParamSchema _ = mempty
281− & type_ ?~ SwaggerString
298+ & type_ ?~ OpenApiString
282 & format ?~ "uuid"
299 & format ?~ "uuid"
283
300
284−-- | A configurable generic @'ParamSchema'@ creator.
301+-- | A configurable generic @'Schema'@ creator.
285--
302--
286-- >>> :set -XDeriveGeneric
303-- >>> :set -XDeriveGeneric
287-- >>> data Color = Red | Blue deriving Generic
304-- >>> data Color = Red | Blue deriving Generic
288−-- >>> encode $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
305+-- >>> BSL.putStrLn $ encodePretty $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
289−-- "{\"type\":\"string\",\"enum\":[\"Red\",\"Blue\"]}"
306+-- {
307+-- "enum": [
308+-- "Red",
309+-- "Blue"
310+-- ],
311+-- "type": "string"
312+-- }
290−genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> ParamSchema t
313+genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> Schema
291genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty
314genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty
292
315
293class GToParamSchema (f :: * -> *) where
316class GToParamSchema (f :: * -> *) where
294− gtoParamSchema :: SchemaOptions -> Proxy f -> ParamSchema t -> ParamSchema t
317+ gtoParamSchema :: SchemaOptions -> Proxy f -> Schema -> Schema
295
318
296instance GToParamSchema f => GToParamSchema (D1 d f) where
319instance GToParamSchema f => GToParamSchema (D1 d f) where
297 gtoParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy f)
320 gtoParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy f)
@@ -309,14 +332,14 @@ instance (GEnumParamSchema f, GEnumParamSchema g) => GToParamSchema (f :+: g) wh
309 gtoParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy (f :+: g))
332 gtoParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy (f :+: g))
310
333
311class GEnumParamSchema (f :: * -> *) where
334class GEnumParamSchema (f :: * -> *) where
312− genumParamSchema :: SchemaOptions -> Proxy f -> ParamSchema t -> ParamSchema t
335+ genumParamSchema :: SchemaOptions -> Proxy f -> Schema -> Schema
313
336
314instance (GEnumParamSchema f, GEnumParamSchema g) => GEnumParamSchema (f :+: g) where
337instance (GEnumParamSchema f, GEnumParamSchema g) => GEnumParamSchema (f :+: g) where
315 genumParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy f) . genumParamSchema opts (Proxy :: Proxy g)
338 genumParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy f) . genumParamSchema opts (Proxy :: Proxy g)
316
339
317instance Constructor c => GEnumParamSchema (C1 c U1) where
340instance Constructor c => GEnumParamSchema (C1 c U1) where
318 genumParamSchema opts _ s = s
341 genumParamSchema opts _ s = s
319− & type_ ?~ SwaggerString
342+ & type_ ?~ OpenApiString
320 & enum_ %~ addEnumValue tag
343 & enum_ %~ addEnumValue tag
321 where
344 where
322 tag = toJSON (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
345 tag = toJSON (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
@@ -328,3 +351,4 @@ data Proxy3 a b c = Proxy3
328
351
329-- $setup
352-- $setup
330-- >>> import Data.Aeson (encode)
353-- >>> import Data.Aeson (encode)
354+-- >>> import Data.OpenApi.Internal.Utils
src/Data/Swagger/Internal/Schema.hs → src/Data/OpenApi/Internal/Schema.hsrenamed+446−172
@@ -1,15 +1,17 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DefaultSignatures #-}
4{-# LANGUAGE DefaultSignatures #-}
3{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE KindSignatures #-}
7{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE DataKinds #-}
7{-# LANGUAGE OverloadedLists #-}
8{-# LANGUAGE OverloadedLists #-}
8{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE PackageImports #-}
10{-# LANGUAGE PackageImports #-}
10{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TupleSections #-}
13{-# LANGUAGE TupleSections #-}
14+{-# LANGUAGE TypeApplications #-}
13{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeFamilies #-}
14{-# LANGUAGE TypeOperators #-}
16{-# LANGUAGE TypeOperators #-}
15{-# LANGUAGE TypeSynonymInstances #-}
17{-# LANGUAGE TypeSynonymInstances #-}
@@ -17,17 +19,18 @@
17{-# OPTIONS_GHC -Wno-redundant-constraints #-}
19{-# OPTIONS_GHC -Wno-redundant-constraints #-}
18-- For TypeErrors
20-- For TypeErrors
19{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
21{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
20−module Data.Swagger.Internal.Schema where
22+module Data.OpenApi.Internal.Schema where
21
23
22import Prelude ()
24import Prelude ()
23import Prelude.Compat
25import Prelude.Compat
24
26
25−import Control.Lens
27+import Control.Lens hiding (allOf)
26import Data.Data.Lens (template)
28import Data.Data.Lens (template)
27
29
28import Control.Monad
30import Control.Monad
29import Control.Monad.Writer
31import Control.Monad.Writer
30−import Data.Aeson (ToJSON (..), ToJSONKey (..), ToJSONKeyFunction (..), Value (..), Object(..))
32+import Data.Aeson (Object (..), SumEncoding (..), ToJSON (..), ToJSONKey (..),
33+ ToJSONKeyFunction (..), Value (..))
31import Data.Char
34import Data.Char
32import Data.Data (Data)
35import Data.Data (Data)
33import Data.Foldable (traverse_)
36import Data.Foldable (traverse_)
@@ -39,8 +42,10 @@ import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
39import Data.Int
42import Data.Int
40import Data.IntSet (IntSet)
43import Data.IntSet (IntSet)
41import Data.IntMap (IntMap)
44import Data.IntMap (IntMap)
45+import Data.List (sort)
42import Data.List.NonEmpty.Compat (NonEmpty)
46import Data.List.NonEmpty.Compat (NonEmpty)
43import Data.Map (Map)
47import Data.Map (Map)
48+import Data.Maybe (fromMaybe)
44import Data.Proxy
49import Data.Proxy
45import Data.Scientific (Scientific)
50import Data.Scientific (Scientific)
46import Data.Fixed (Fixed, HasResolution, Pico)
51import Data.Fixed (Fixed, HasResolution, Pico)
@@ -57,17 +62,19 @@ import Numeric.Natural.Compat (Natural)
57import Data.Word
62import Data.Word
58import GHC.Generics
63import GHC.Generics
59import qualified Data.UUID.Types as UUID
64import qualified Data.UUID.Types as UUID
65+import Type.Reflection (Typeable, typeRep)
60
66
67+import Data.OpenApi.Aeson.Compat (keyToText, objectKeys, toInsOrdHashMap)
61−import Data.Swagger.Declare
68+import Data.OpenApi.Declare
62−import Data.Swagger.Internal
69+import Data.OpenApi.Internal
63−import Data.Swagger.Internal.ParamSchema (ToParamSchema(..))
70+import Data.OpenApi.Internal.ParamSchema (ToParamSchema (..))
71+import Data.OpenApi.Internal.TypeShape
64−import Data.Swagger.Lens hiding (name, schema)
72+import Data.OpenApi.Lens hiding (name, schema)
65−import qualified Data.Swagger.Lens as Swagger
73+import qualified Data.OpenApi.Lens as Swagger
66−import Data.Swagger.SchemaOptions
74+import Data.OpenApi.SchemaOptions
67−import Data.Swagger.Internal.TypeShape
68
75
69import qualified Data.ByteString as BS
76import qualified Data.ByteString as BS
70−import qualified Data.ByteString.Lazy as BSL
77+import qualified Data.ByteString.Lazy.Char8 as BSL
71import GHC.TypeLits (TypeError, ErrorMessage(..))
78import GHC.TypeLits (TypeError, ErrorMessage(..))
72
79
73unnamed :: Schema -> NamedSchema
80unnamed :: Schema -> NamedSchema
@@ -95,7 +102,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
95--
102--
96-- import Control.Lens
103-- import Control.Lens
97-- import Data.Proxy
104-- import Data.Proxy
98−-- import Data.Swagger
105+-- import Data.OpenApi
99--
106--
100-- data Coord = Coord { x :: Double, y :: Double }
107-- data Coord = Coord { x :: Double, y :: Double }
101--
108--
@@ -103,7 +110,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
103-- declareNamedSchema _ = do
110-- declareNamedSchema _ = do
104-- doubleSchema <- declareSchemaRef (Proxy :: Proxy Double)
111-- doubleSchema <- declareSchemaRef (Proxy :: Proxy Double)
105-- return $ NamedSchema (Just \"Coord\") $ mempty
112-- return $ NamedSchema (Just \"Coord\") $ mempty
106−-- & type_ ?~ SwaggerObject
113+-- & type_ ?~ OpenApiObject
107-- & properties .~
114-- & properties .~
108-- [ (\"x\", doubleSchema)
115-- [ (\"x\", doubleSchema)
109-- , (\"y\", doubleSchema)
116-- , (\"y\", doubleSchema)
@@ -129,13 +136,13 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
129--
136--
130-- instance ToSchema Coord
137-- instance ToSchema Coord
131-- @
138-- @
132−class ToSchema a where
139+class Typeable a => ToSchema a where
133 -- | Convert a type into an optionally named schema
140 -- | Convert a type into an optionally named schema
134 -- together with all used definitions.
141 -- together with all used definitions.
135 -- Note that the schema itself is included in definitions
142 -- Note that the schema itself is included in definitions
136 -- only if it is recursive (and thus needs its definition in scope).
143 -- only if it is recursive (and thus needs its definition in scope).
137 declareNamedSchema :: Proxy a -> Declare (Definitions Schema) NamedSchema
144 declareNamedSchema :: Proxy a -> Declare (Definitions Schema) NamedSchema
138− default declareNamedSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted") =>
145+ default declareNamedSchema :: (Generic a, GToSchema (Rep a)) =>
139 Proxy a -> Declare (Definitions Schema) NamedSchema
146 Proxy a -> Declare (Definitions Schema) NamedSchema
140 declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
147 declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
141
148
@@ -151,13 +158,19 @@ declareSchema = fmap _namedSchemaSchema . declareNamedSchema
151--
158--
152-- >>> toNamedSchema (Proxy :: Proxy String) ^. name
159-- >>> toNamedSchema (Proxy :: Proxy String) ^. name
153-- Nothing
160-- Nothing
154−-- >>> encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
161+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy String) ^. schema)
162+-- {
155−-- "{\"type\":\"string\"}"
163+-- "type": "string"
164+-- }
156--
165--
157-- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
166-- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
158-- Just "Day"
167-- Just "Day"
159−-- >>> encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
168+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy Day) ^. schema)
169+-- {
160−-- "{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}"
170+-- "example": "2016-07-22",
171+-- "format": "date",
172+-- "type": "string"
173+-- }
161toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
174toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
162toNamedSchema = undeclare . declareNamedSchema
175toNamedSchema = undeclare . declareNamedSchema
163
176
@@ -173,22 +186,35 @@ schemaName = _namedSchemaName . toNamedSchema
173
186
174-- | Convert a type into a schema.
187-- | Convert a type into a schema.
175--
188--
176−-- >>> encode $ toSchema (Proxy :: Proxy Int8)
189+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Int8)
190+-- {
191+-- "maximum": 127,
192+-- "minimum": -128,
177−-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
193+-- "type": "integer"
194+-- }
178--
195--
179−-- >>> encode $ toSchema (Proxy :: Proxy [Day])
196+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy [Day])
197+-- {
198+-- "items": {
199+-- "$ref": "#/components/schemas/Day"
200+-- },
180−-- "{\"items\":{\"$ref\":\"#/definitions/Day\"},\"type\":\"array\"}"
201+-- "type": "array"
202+-- }
181toSchema :: ToSchema a => Proxy a -> Schema
203toSchema :: ToSchema a => Proxy a -> Schema
182toSchema = _namedSchemaSchema . toNamedSchema
204toSchema = _namedSchemaSchema . toNamedSchema
183
205
184-- | Convert a type into a referenced schema if possible.
206-- | Convert a type into a referenced schema if possible.
185-- Only named schemas can be referenced, nameless schemas are inlined.
207-- Only named schemas can be referenced, nameless schemas are inlined.
186--
208--
187−-- >>> encode $ toSchemaRef (Proxy :: Proxy Integer)
209+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Integer)
210+-- {
188−-- "{\"type\":\"integer\"}"
211+-- "type": "integer"
212+-- }
189--
213--
190−-- >>> encode $ toSchemaRef (Proxy :: Proxy Day)
214+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Day)
191−-- "{\"$ref\":\"#/definitions/Day\"}"
215+-- {
216+-- "$ref": "#/components/schemas/Day"
217+-- }
192toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
218toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
193toSchemaRef = undeclare . declareSchemaRef
219toSchemaRef = undeclare . declareSchemaRef
194
220
@@ -256,8 +282,15 @@ inlineAllSchemas = inlineSchemasWhen (const True)
256
282
257-- | Convert a type into a schema without references.
283-- | Convert a type into a schema without references.
258--
284--
259−-- >>> encode $ toInlinedSchema (Proxy :: Proxy [Day])
285+-- >>> BSL.putStrLn $ encodePretty $ toInlinedSchema (Proxy :: Proxy [Day])
286+-- {
287+-- "items": {
260−-- "{\"items\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"},\"type\":\"array\"}"
288+-- "example": "2016-07-22",
289+-- "format": "date",
290+-- "type": "string"
291+-- },
292+-- "type": "array"
293+-- }
261--
294--
262-- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
295-- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
263-- when inlining recursive schemas.
296-- when inlining recursive schemas.
@@ -287,41 +320,67 @@ inlineNonRecursiveSchemas defs = inlineSchemasWhen nonRecursive defs
287 traverse_ usedNames (InsOrdHashMap.lookup name defs)
320 traverse_ usedNames (InsOrdHashMap.lookup name defs)
288 Inline subschema -> usedNames subschema
321 Inline subschema -> usedNames subschema
289
322
290−-- | Default schema for binary data (any sequence of octets).
291−binarySchema :: Schema
292−binarySchema = mempty
293− & type_ ?~ SwaggerString
294− & format ?~ "binary"
295−
296−-- | Default schema for binary data (base64 encoded).
297−byteSchema :: Schema
298−byteSchema = mempty
299− & type_ ?~ SwaggerString
300− & format ?~ "byte"
301−
302−-- | Default schema for password string.
303−-- @"password"@ format is used to hint UIs the input needs to be obscured.
304−passwordSchema :: Schema
305−passwordSchema = mempty
306− & type_ ?~ SwaggerString
307− & format ?~ "password"
308−
309-- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
323-- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
310-- Produced schema can be used for further refinement.
324-- Produced schema can be used for further refinement.
311--
325--
312−-- >>> encode $ sketchSchema "hello"
326+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema "hello"
327+-- {
313−-- "{\"example\":\"hello\",\"type\":\"string\"}"
328+-- "example": "hello",
329+-- "type": "string"
330+-- }
314--
331--
315−-- >>> encode $ sketchSchema (1, 2, 3)
332+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (1, 2, 3)
333+-- {
334+-- "example": [
335+-- 1,
336+-- 2,
337+-- 3
338+-- ],
339+-- "items": {
316−-- "{\"example\":[1,2,3],\"items\":{\"type\":\"number\"},\"type\":\"array\"}"
340+-- "type": "number"
341+-- },
342+-- "type": "array"
343+-- }
317--
344--
318−-- >>> encode $ sketchSchema ("Jack", 25)
345+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema ("Jack", 25)
319−-- "{\"example\":[\"Jack\",25],\"items\":[{\"type\":\"string\"},{\"type\":\"number\"}],\"type\":\"array\"}"
346+-- {
347+-- "example": [
348+-- "Jack",
349+-- 25
350+-- ],
351+-- "items": [
352+-- {
353+-- "type": "string"
354+-- },
355+-- {
356+-- "type": "number"
357+-- }
358+-- ],
359+-- "type": "array"
360+-- }
320--
361--
321-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
362-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
322-- >>> instance ToJSON Person
363-- >>> instance ToJSON Person
323−-- >>> encode $ sketchSchema (Person "Jack" 25)
364+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (Person "Jack" 25)
324−-- "{\"required\":[\"age\",\"name\"],\"properties\":{\"age\":{\"type\":\"number\"},\"name\":{\"type\":\"string\"}},\"example\":{\"age\":25,\"name\":\"Jack\"},\"type\":\"object\"}"
365+-- {
366+-- "example": {
367+-- "age": 25,
368+-- "name": "Jack"
369+-- },
370+-- "properties": {
371+-- "age": {
372+-- "type": "number"
373+-- },
374+-- "name": {
375+-- "type": "string"
376+-- }
377+-- },
378+-- "required": [
379+-- "age",
380+-- "name"
381+-- ],
382+-- "type": "object"
383+-- }
325sketchSchema :: ToJSON a => a -> Schema
384sketchSchema :: ToJSON a => a -> Schema
326sketchSchema = sketch . toJSON
385sketchSchema = sketch . toJSON
327 where
386 where
@@ -329,15 +388,15 @@ sketchSchema = sketch . toJSON
329 sketch js@(Bool _) = go js
388 sketch js@(Bool _) = go js
330 sketch js = go js & example ?~ js
389 sketch js = go js & example ?~ js
331
390
332− go Null = mempty & type_ ?~ SwaggerNull
391+ go Null = mempty & type_ ?~ OpenApiNull
333− go (Bool _) = mempty & type_ ?~ SwaggerBoolean
392+ go (Bool _) = mempty & type_ ?~ OpenApiBoolean
334− go (String _) = mempty & type_ ?~ SwaggerString
393+ go (String _) = mempty & type_ ?~ OpenApiString
335− go (Number _) = mempty & type_ ?~ SwaggerNumber
394+ go (Number _) = mempty & type_ ?~ OpenApiNumber
336 go (Array xs) = mempty
395 go (Array xs) = mempty
337− & type_ ?~ SwaggerArray
396+ & type_ ?~ OpenApiArray
338 & items ?~ case ischema of
397 & items ?~ case ischema of
339− Just s -> SwaggerItemsObject (Inline s)
398+ Just s -> OpenApiItemsObject (Inline s)
340− _ -> SwaggerItemsArray (map Inline ys)
399+ _ -> OpenApiItemsArray (map Inline ys)
341 where
400 where
342 ys = map go (V.toList xs)
401 ys = map go (V.toList xs)
343 allSame = and ((zipWith (==)) ys (tail ys))
402 allSame = and ((zipWith (==)) ys (tail ys))
@@ -346,64 +405,178 @@ sketchSchema = sketch . toJSON
346 (z:_) | allSame -> Just z
405 (z:_) | allSame -> Just z
347 _ -> Nothing
406 _ -> Nothing
348 go (Object o) = mempty
407 go (Object o) = mempty
349− & type_ ?~ SwaggerObject
408+ & type_ ?~ OpenApiObject
350− & required .~ HashMap.keys o
409+ & required .~ sort (objectKeys o)
351− & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
410+ & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
352
411
353-- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
412-- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
354-- Produced schema uses as much constraints as possible.
413-- Produced schema uses as much constraints as possible.
355--
414--
356−-- >>> encode $ sketchStrictSchema "hello"
415+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema "hello"
357−-- "{\"maxLength\":5,\"pattern\":\"hello\",\"minLength\":5,\"type\":\"string\",\"enum\":[\"hello\"]}"
416+-- {
417+-- "enum": [
418+-- "hello"
419+-- ],
420+-- "maxLength": 5,
421+-- "minLength": 5,
422+-- "pattern": "hello",
423+-- "type": "string"
424+-- }
358--
425--
359−-- >>> encode $ sketchStrictSchema (1, 2, 3)
426+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (1, 2, 3)
360−-- "{\"minItems\":3,\"uniqueItems\":true,\"items\":[{\"maximum\":1,\"minimum\":1,\"multipleOf\":1,\"type\":\"number\",\"enum\":[1]},{\"maximum\":2,\"minimum\":2,\"multipleOf\":2,\"type\":\"number\",\"enum\":[2]},{\"maximum\":3,\"minimum\":3,\"multipleOf\":3,\"type\":\"number\",\"enum\":[3]}],\"maxItems\":3,\"type\":\"array\",\"enum\":[[1,2,3]]}"
427+-- {
428+-- "enum": [
429+-- [
430+-- 1,
431+-- 2,
432+-- 3
433+-- ]
434+-- ],
435+-- "items": [
436+-- {
437+-- "enum": [
438+-- 1
439+-- ],
440+-- "maximum": 1,
441+-- "minimum": 1,
442+-- "multipleOf": 1,
443+-- "type": "number"
444+-- },
445+-- {
446+-- "enum": [
447+-- 2
448+-- ],
449+-- "maximum": 2,
450+-- "minimum": 2,
451+-- "multipleOf": 2,
452+-- "type": "number"
453+-- },
454+-- {
455+-- "enum": [
456+-- 3
457+-- ],
458+-- "maximum": 3,
459+-- "minimum": 3,
460+-- "multipleOf": 3,
461+-- "type": "number"
462+-- }
463+-- ],
464+-- "maxItems": 3,
465+-- "minItems": 3,
466+-- "type": "array",
467+-- "uniqueItems": true
468+-- }
361--
469--
362−-- >>> encode $ sketchStrictSchema ("Jack", 25)
470+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema ("Jack", 25)
363−-- "{\"minItems\":2,\"uniqueItems\":true,\"items\":[{\"maxLength\":4,\"pattern\":\"Jack\",\"minLength\":4,\"type\":\"string\",\"enum\":[\"Jack\"]},{\"maximum\":25,\"minimum\":25,\"multipleOf\":25,\"type\":\"number\",\"enum\":[25]}],\"maxItems\":2,\"type\":\"array\",\"enum\":[[\"Jack\",25]]}"
471+-- {
472+-- "enum": [
473+-- [
474+-- "Jack",
475+-- 25
476+-- ]
477+-- ],
478+-- "items": [
479+-- {
480+-- "enum": [
481+-- "Jack"
482+-- ],
483+-- "maxLength": 4,
484+-- "minLength": 4,
485+-- "pattern": "Jack",
486+-- "type": "string"
487+-- },
488+-- {
489+-- "enum": [
490+-- 25
491+-- ],
492+-- "maximum": 25,
493+-- "minimum": 25,
494+-- "multipleOf": 25,
495+-- "type": "number"
496+-- }
497+-- ],
498+-- "maxItems": 2,
499+-- "minItems": 2,
500+-- "type": "array",
501+-- "uniqueItems": true
502+-- }
364--
503--
365-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
504-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
366-- >>> instance ToJSON Person
505-- >>> instance ToJSON Person
367−-- >>> encode $ sketchStrictSchema (Person "Jack" 25)
506+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (Person "Jack" 25)
368−-- "{\"required\":[\"age\",\"name\"],\"properties\":{\"age\":{\"maximum\":25,\"minimum\":25,\"multipleOf\":25,\"type\":\"number\",\"enum\":[25]},\"name\":{\"maxLength\":4,\"pattern\":\"Jack\",\"minLength\":4,\"type\":\"string\",\"enum\":[\"Jack\"]}},\"maxProperties\":2,\"minProperties\":2,\"type\":\"object\",\"enum\":[{\"age\":25,\"name\":\"Jack\"}]}"
507+-- {
508+-- "enum": [
509+-- {
510+-- "age": 25,
511+-- "name": "Jack"
512+-- }
513+-- ],
514+-- "maxProperties": 2,
515+-- "minProperties": 2,
516+-- "properties": {
517+-- "age": {
518+-- "enum": [
519+-- 25
520+-- ],
521+-- "maximum": 25,
522+-- "minimum": 25,
523+-- "multipleOf": 25,
524+-- "type": "number"
525+-- },
526+-- "name": {
527+-- "enum": [
528+-- "Jack"
529+-- ],
530+-- "maxLength": 4,
531+-- "minLength": 4,
532+-- "pattern": "Jack",
533+-- "type": "string"
534+-- }
535+-- },
536+-- "required": [
537+-- "age",
538+-- "name"
539+-- ],
540+-- "type": "object"
541+-- }
369sketchStrictSchema :: ToJSON a => a -> Schema
542sketchStrictSchema :: ToJSON a => a -> Schema
370sketchStrictSchema = go . toJSON
543sketchStrictSchema = go . toJSON
371 where
544 where
372− go Null = mempty & type_ ?~ SwaggerNull
545+ go Null = mempty & type_ ?~ OpenApiNull
373 go js@(Bool _) = mempty
546 go js@(Bool _) = mempty
374− & type_ ?~ SwaggerBoolean
547+ & type_ ?~ OpenApiBoolean
375 & enum_ ?~ [js]
548 & enum_ ?~ [js]
376 go js@(String s) = mempty
549 go js@(String s) = mempty
377− & type_ ?~ SwaggerString
550+ & type_ ?~ OpenApiString
378 & maxLength ?~ fromIntegral (T.length s)
551 & maxLength ?~ fromIntegral (T.length s)
379 & minLength ?~ fromIntegral (T.length s)
552 & minLength ?~ fromIntegral (T.length s)
380 & pattern ?~ s
553 & pattern ?~ s
381 & enum_ ?~ [js]
554 & enum_ ?~ [js]
382 go js@(Number n) = mempty
555 go js@(Number n) = mempty
383− & type_ ?~ SwaggerNumber
556+ & type_ ?~ OpenApiNumber
384 & maximum_ ?~ n
557 & maximum_ ?~ n
385 & minimum_ ?~ n
558 & minimum_ ?~ n
386 & multipleOf ?~ n
559 & multipleOf ?~ n
387 & enum_ ?~ [js]
560 & enum_ ?~ [js]
388 go js@(Array xs) = mempty
561 go js@(Array xs) = mempty
389− & type_ ?~ SwaggerArray
562+ & type_ ?~ OpenApiArray
390 & maxItems ?~ fromIntegral sz
563 & maxItems ?~ fromIntegral sz
391 & minItems ?~ fromIntegral sz
564 & minItems ?~ fromIntegral sz
392− & items ?~ SwaggerItemsArray (map (Inline . go) (V.toList xs))
565+ & items ?~ OpenApiItemsArray (map (Inline . go) (V.toList xs))
393 & uniqueItems ?~ allUnique
566 & uniqueItems ?~ allUnique
394 & enum_ ?~ [js]
567 & enum_ ?~ [js]
395 where
568 where
396 sz = length xs
569 sz = length xs
397 allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs))
570 allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs))
398 go js@(Object o) = mempty
571 go js@(Object o) = mempty
399− & type_ ?~ SwaggerObject
572+ & type_ ?~ OpenApiObject
400− & required .~ names
573+ & required .~ sort names
401− & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
574+ & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
402 & maxProperties ?~ fromIntegral (length names)
575 & maxProperties ?~ fromIntegral (length names)
403 & minProperties ?~ fromIntegral (length names)
576 & minProperties ?~ fromIntegral (length names)
404 & enum_ ?~ [js]
577 & enum_ ?~ [js]
405 where
578 where
406− names = HashMap.keys o
579+ names = objectKeys o
407
580
408class GToSchema (f :: * -> *) where
581class GToSchema (f :: * -> *) where
409 gdeclareNamedSchema :: SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
582 gdeclareNamedSchema :: SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
@@ -412,8 +585,8 @@ instance {-# OVERLAPPABLE #-} ToSchema a => ToSchema [a] where
412 declareNamedSchema _ = do
585 declareNamedSchema _ = do
413 ref <- declareSchemaRef (Proxy :: Proxy a)
586 ref <- declareSchemaRef (Proxy :: Proxy a)
414 return $ unnamed $ mempty
587 return $ unnamed $ mempty
415− & type_ ?~ SwaggerArray
588+ & type_ ?~ OpenApiArray
416− & items ?~ SwaggerItemsObject ref
589+ & items ?~ OpenApiItemsObject ref
417
590
418instance {-# OVERLAPPING #-} ToSchema String where declareNamedSchema = plain . paramSchemaToSchema
591instance {-# OVERLAPPING #-} ToSchema String where declareNamedSchema = plain . paramSchemaToSchema
419instance ToSchema Bool where declareNamedSchema = plain . paramSchemaToSchema
592instance ToSchema Bool where declareNamedSchema = plain . paramSchemaToSchema
@@ -438,12 +611,14 @@ instance ToSchema Scientific where declareNamedSchema = plain . paramSchemaToSc
438instance ToSchema Double where declareNamedSchema = plain . paramSchemaToSchema
611instance ToSchema Double where declareNamedSchema = plain . paramSchemaToSchema
439instance ToSchema Float where declareNamedSchema = plain . paramSchemaToSchema
612instance ToSchema Float where declareNamedSchema = plain . paramSchemaToSchema
440
613
441−instance HasResolution a => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
614+instance (Typeable (Fixed a), HasResolution a) => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
442
615
443instance ToSchema a => ToSchema (Maybe a) where
616instance ToSchema a => ToSchema (Maybe a) where
444 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
617 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
445
618
446−instance (ToSchema a, ToSchema b) => ToSchema (Either a b)
619+instance (ToSchema a, ToSchema b) => ToSchema (Either a b) where
620+ -- To match Aeson instance
621+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions { sumEncoding = ObjectWithSingleField }
447
622
448instance ToSchema () where
623instance ToSchema () where
449 declareNamedSchema _ = pure (NamedSchema Nothing nullarySchema)
624 declareNamedSchema _ = pure (NamedSchema Nothing nullarySchema)
@@ -453,16 +628,22 @@ instance ToSchema UUID.UUID where
453 declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p
628 declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p
454 & example ?~ toJSON (UUID.toText UUID.nil)
629 & example ?~ toJSON (UUID.toText UUID.nil)
455
630
456−instance (ToSchema a, ToSchema b) => ToSchema (a, b)
631+instance (ToSchema a, ToSchema b) => ToSchema (a, b) where
632+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
457−instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c)
633+instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c) where
634+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
458−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d)
635+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d) where
636+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
459−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e)
637+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e) where
638+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
460−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f)
639+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f) where
640+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
461−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g)
641+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g) where
642+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
462
643
463timeSchema :: T.Text -> Schema
644timeSchema :: T.Text -> Schema
464timeSchema fmt = mempty
645timeSchema fmt = mempty
465− & type_ ?~ SwaggerString
646+ & type_ ?~ OpenApiString
466 & format ?~ fmt
647 & format ?~ fmt
467
648
468-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
649-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
@@ -477,7 +658,7 @@ instance ToSchema LocalTime where
477 declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss"
658 declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss"
478 & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0))
659 & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0))
479
660
480−-- | Format @"date"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
661+-- | Format @"date-time"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
481instance ToSchema ZonedTime where
662instance ToSchema ZonedTime where
482 declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time"
663 declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time"
483 & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))
664 & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))
@@ -509,7 +690,7 @@ instance ToSchemaByteStringError BSL.ByteString => ToSchema BSL.ByteString where
509instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int))
690instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int))
510
691
511-- | NOTE: This schema does not account for the uniqueness of keys.
692-- | NOTE: This schema does not account for the uniqueness of keys.
512−instance ToSchema a => ToSchema (IntMap a) where
693+instance (ToSchema a) => ToSchema (IntMap a) where
513 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
694 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
514
695
515instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
696instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
@@ -520,7 +701,7 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
520 declareObjectMapSchema = do
701 declareObjectMapSchema = do
521 schema <- declareSchemaRef (Proxy :: Proxy v)
702 schema <- declareSchemaRef (Proxy :: Proxy v)
522 return $ unnamed $ mempty
703 return $ unnamed $ mempty
523− & type_ ?~ SwaggerObject
704+ & type_ ?~ OpenApiObject
524 & additionalProperties ?~ AdditionalPropertiesSchema schema
705 & additionalProperties ?~ AdditionalPropertiesSchema schema
525
706
526instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
707instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
@@ -528,7 +709,7 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
528
709
529instance {-# OVERLAPPING #-} ToSchema Object where
710instance {-# OVERLAPPING #-} ToSchema Object where
530 declareNamedSchema _ = pure $ NamedSchema (Just "Object") $ mempty
711 declareNamedSchema _ = pure $ NamedSchema (Just "Object") $ mempty
531− & type_ ?~ SwaggerObject
712+ & type_ ?~ OpenApiObject
532 & description ?~ "Arbitrary JSON object."
713 & description ?~ "Arbitrary JSON object."
533 & additionalProperties ?~ AdditionalPropertiesAllowed True
714 & additionalProperties ?~ AdditionalPropertiesAllowed True
534
715
@@ -565,11 +746,15 @@ instance ToSchema a => ToSchema (Identity a) where declareNamedSchema _ = declar
565
746
566-- | Default schema for @'Bounded'@, @'Integral'@ types.
747-- | Default schema for @'Bounded'@, @'Integral'@ types.
567--
748--
568−-- >>> encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
749+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
750+-- {
569−-- "{\"maximum\":32767,\"minimum\":-32768,\"type\":\"integer\"}"
751+-- "maximum": 32767,
752+-- "minimum": -32768,
753+-- "type": "integer"
754+-- }
570toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
755toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
571toSchemaBoundedIntegral _ = mempty
756toSchemaBoundedIntegral _ = mempty
572− & type_ ?~ SwaggerInteger
757+ & type_ ?~ OpenApiInteger
573 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
758 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
574 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
759 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
575
760
@@ -598,8 +783,27 @@ genericDeclareNamedSchemaNewtype opts f proxy = genericNameSchema opts proxy <$>
598-- >>> instance ToSchema ButtonState
783-- >>> instance ToSchema ButtonState
599-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
784-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
600-- >>> type ImageUrl = T.Text
785-- >>> type ImageUrl = T.Text
601−-- >>> encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
786+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
602−-- "{\"properties\":{\"Neutral\":{\"type\":\"string\"},\"Focus\":{\"type\":\"string\"},\"Active\":{\"type\":\"string\"},\"Hover\":{\"type\":\"string\"},\"Disabled\":{\"type\":\"string\"}},\"type\":\"object\"}"
787+-- {
788+-- "properties": {
789+-- "Active": {
790+-- "type": "string"
791+-- },
792+-- "Disabled": {
793+-- "type": "string"
794+-- },
795+-- "Focus": {
796+-- "type": "string"
797+-- },
798+-- "Hover": {
799+-- "type": "string"
800+-- },
801+-- "Neutral": {
802+-- "type": "string"
803+-- }
804+-- },
805+-- "type": "object"
806+-- }
603--
807--
604-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
808-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
605-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
809-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -607,15 +811,15 @@ declareSchemaBoundedEnumKeyMapping :: forall map key value.
607 (Bounded key, Enum key, ToJSONKey key, ToSchema key, ToSchema value)
811 (Bounded key, Enum key, ToJSONKey key, ToSchema key, ToSchema value)
608 => Proxy (map key value) -> Declare (Definitions Schema) Schema
812 => Proxy (map key value) -> Declare (Definitions Schema) Schema
609declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key of
813declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key of
610− ToJSONKeyText keyToText _ -> objectSchema keyToText
814+ ToJSONKeyText getKey _ -> objectSchema getKey
611 ToJSONKeyValue _ _ -> declareSchema (Proxy :: Proxy [(key, value)])
815 ToJSONKeyValue _ _ -> declareSchema (Proxy :: Proxy [(key, value)])
612 where
816 where
613− objectSchema keyToText = do
817+ objectSchema getKey = do
614 valueRef <- declareSchemaRef (Proxy :: Proxy value)
818 valueRef <- declareSchemaRef (Proxy :: Proxy value)
615 let allKeys = [minBound..maxBound :: key]
819 let allKeys = [minBound..maxBound :: key]
616− mkPair k = (keyToText k, valueRef)
820+ mkPair k = (keyToText $ getKey k, valueRef)
617 return $ mempty
821 return $ mempty
618− & type_ ?~ SwaggerObject
822+ & type_ ?~ OpenApiObject
619 & properties .~ InsOrdHashMap.fromList (map mkPair allKeys)
823 & properties .~ InsOrdHashMap.fromList (map mkPair allKeys)
620
824
621-- | A 'Schema' for a mapping with 'Bounded' 'Enum' keys.
825-- | A 'Schema' for a mapping with 'Bounded' 'Enum' keys.
@@ -626,8 +830,27 @@ declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key o
626-- >>> instance ToSchema ButtonState
830-- >>> instance ToSchema ButtonState
627-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
831-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
628-- >>> type ImageUrl = T.Text
832-- >>> type ImageUrl = T.Text
629−-- >>> encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
833+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
630−-- "{\"properties\":{\"Neutral\":{\"type\":\"string\"},\"Focus\":{\"type\":\"string\"},\"Active\":{\"type\":\"string\"},\"Hover\":{\"type\":\"string\"},\"Disabled\":{\"type\":\"string\"}},\"type\":\"object\"}"
834+-- {
835+-- "properties": {
836+-- "Active": {
837+-- "type": "string"
838+-- },
839+-- "Disabled": {
840+-- "type": "string"
841+-- },
842+-- "Focus": {
843+-- "type": "string"
844+-- },
845+-- "Hover": {
846+-- "type": "string"
847+-- },
848+-- "Neutral": {
849+-- "type": "string"
850+-- }
851+-- },
852+-- "type": "object"
853+-- }
631--
854--
632-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
855-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
633-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
856-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -637,32 +860,32 @@ toSchemaBoundedEnumKeyMapping :: forall map key value.
637toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping
860toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping
638
861
639-- | A configurable generic @'Schema'@ creator.
862-- | A configurable generic @'Schema'@ creator.
640−genericDeclareSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareSchemaUnrestricted") =>
863+genericDeclareSchema :: (Generic a, GToSchema (Rep a), Typeable a) =>
641 SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
864 SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
642−genericDeclareSchema = genericDeclareSchemaUnrestricted
865+genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchema opts proxy
643
866
644-- | A configurable generic @'NamedSchema'@ creator.
867-- | A configurable generic @'NamedSchema'@ creator.
645-- This function applied to @'defaultSchemaOptions'@
868-- This function applied to @'defaultSchemaOptions'@
646-- is used as the default for @'declareNamedSchema'@
869-- is used as the default for @'declareNamedSchema'@
647-- when the type is an instance of @'Generic'@.
870-- when the type is an instance of @'Generic'@.
648−genericDeclareNamedSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted") =>
649− SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
650−genericDeclareNamedSchema = genericDeclareNamedSchemaUnrestricted
651−
652−-- | A configurable generic @'Schema'@ creator.
653--
871--
654−-- Unlike 'genericDeclareSchema' also works for mixed sum types.
872+-- Default implementation will use the name from 'Typeable' instance, including concrete
655−-- Use with care since some Swagger tools do not support well schemas for mixed sum types.
873+-- instantioations of type variables.
656−genericDeclareSchemaUnrestricted :: (Generic a, GToSchema (Rep a)) => SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
657−genericDeclareSchemaUnrestricted opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchemaUnrestricted opts proxy
658−
659−-- | A configurable generic @'NamedSchema'@ creator.
660--
874--
661−-- Unlike 'genericDeclareNamedSchema' also works for mixed sum types.
875+-- For example:
662−-- Use with care since some Swagger tools do not support well schemas for mixed sum types.
876+--
877+-- >>> _namedSchemaName $ undeclare $ genericDeclareNamedSchema defaultSchemaOptions (Proxy :: Proxy (Either Int Bool))
878+-- Just "Either_Int_Bool"
663−genericDeclareNamedSchemaUnrestricted :: forall a. (Generic a, GToSchema (Rep a)) =>
879+genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a), Typeable a) =>
664 SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
880 SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
881+genericDeclareNamedSchema opts _ =
665−genericDeclareNamedSchemaUnrestricted opts _ = gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
882+ rename (Just $ T.pack name) <$> gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
883+ where
884+ unspace ' ' = '_'
885+ unspace x = x
886+ orig = fmap unspace $ show $ typeRep @a
887+ name = datatypeNameModifier opts orig
888+
666
889
667-- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'.
890-- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'.
668genericNameSchema :: forall a d f.
891genericNameSchema :: forall a d f.
@@ -678,19 +901,19 @@ gdatatypeSchemaName opts _ = case orig of
678 orig = datatypeName (Proxy3 :: Proxy3 d f a)
901 orig = datatypeName (Proxy3 :: Proxy3 d f a)
679 name = datatypeNameModifier opts orig
902 name = datatypeNameModifier opts orig
680
903
681−-- | Lift a plain @'ParamSchema'@ into a model @'NamedSchema'@.
904+-- | Construct 'NamedSchema' usinng 'ToParamSchema'.
682paramSchemaToNamedSchema :: (ToParamSchema a, Generic a, Rep a ~ D1 d f, Datatype d) =>
905paramSchemaToNamedSchema :: (ToParamSchema a, Generic a, Rep a ~ D1 d f, Datatype d) =>
683 SchemaOptions -> Proxy a -> NamedSchema
906 SchemaOptions -> Proxy a -> NamedSchema
684paramSchemaToNamedSchema opts proxy = genericNameSchema opts proxy (paramSchemaToSchema proxy)
907paramSchemaToNamedSchema opts proxy = genericNameSchema opts proxy (paramSchemaToSchema proxy)
685
908
686−-- | Lift a plain @'ParamSchema'@ into a model @'Schema'@.
909+-- | Construct 'Schema' usinng 'ToParamSchema'.
687paramSchemaToSchema :: ToParamSchema a => Proxy a -> Schema
910paramSchemaToSchema :: ToParamSchema a => Proxy a -> Schema
688−paramSchemaToSchema proxy = mempty & paramSchema .~ toParamSchema proxy
911+paramSchemaToSchema = toParamSchema
689
912
690nullarySchema :: Schema
913nullarySchema :: Schema
691nullarySchema = mempty
914nullarySchema = mempty
692− & type_ ?~ SwaggerArray
915+ & type_ ?~ OpenApiArray
693− & items ?~ SwaggerItemsArray []
916+ & items ?~ OpenApiItemsArray []
694
917
695gtoNamedSchema :: GToSchema f => SchemaOptions -> Proxy f -> NamedSchema
918gtoNamedSchema :: GToSchema f => SchemaOptions -> Proxy f -> NamedSchema
696gtoNamedSchema opts proxy = undeclare $ gdeclareNamedSchema opts proxy mempty
919gtoNamedSchema opts proxy = undeclare $ gdeclareNamedSchema opts proxy mempty
@@ -720,12 +943,15 @@ instance (Selector s, GToSchema f, GToSchema (S1 s f)) => GToSchema (C1 c (S1 s
720 | unwrapUnaryRecords opts = fieldSchema
943 | unwrapUnaryRecords opts = fieldSchema
721 | otherwise =
944 | otherwise =
722 case schema ^. items of
945 case schema ^. items of
723− Just (SwaggerItemsArray [_]) -> fieldSchema
946+ Just (OpenApiItemsArray [_]) -> fieldSchema
724 _ -> do
947 _ -> do
725− declare defs
948+ -- We have to run recordSchema instead of just using its defs,
949+ -- since those can be recursive and will lead to infinite loop,
950+ -- see https://github.com/biocad/openapi3/pull/37
951+ NamedSchema _ schema' <- recordSchema
726− return (unnamed schema)
952+ return (unnamed schema')
727 where
953 where
728− (defs, NamedSchema _ schema) = runDeclare recordSchema mempty
954+ (_, NamedSchema _ schema) = runDeclare recordSchema mempty
729 recordSchema = gdeclareNamedSchema opts (Proxy :: Proxy (S1 s f)) s
955 recordSchema = gdeclareNamedSchema opts (Proxy :: Proxy (S1 s f)) s
730 fieldSchema = gdeclareNamedSchema opts (Proxy :: Proxy f) s
956 fieldSchema = gdeclareNamedSchema opts (Proxy :: Proxy f) s
731
957
@@ -748,10 +974,10 @@ gdeclareSchemaRef opts proxy = do
748 return $ Ref (Reference name)
974 return $ Ref (Reference name)
749 _ -> Inline <$> gdeclareSchema opts proxy
975 _ -> Inline <$> gdeclareSchema opts proxy
750
976
751−appendItem :: Referenced Schema -> Maybe (SwaggerItems 'SwaggerKindSchema) -> Maybe (SwaggerItems 'SwaggerKindSchema)
977+appendItem :: Referenced Schema -> Maybe OpenApiItems -> Maybe OpenApiItems
752−appendItem x Nothing = Just (SwaggerItemsArray [x])
978+appendItem x Nothing = Just (OpenApiItemsArray [x])
753−appendItem x (Just (SwaggerItemsArray xs)) = Just (SwaggerItemsArray (xs ++ [x]))
979+appendItem x (Just (OpenApiItemsArray xs)) = Just (OpenApiItemsArray (xs ++ [x]))
754−appendItem _ _ = error "GToSchema.appendItem: cannot append to SwaggerItemsObject"
980+appendItem _ _ = error "GToSchema.appendItem: cannot append to OpenApiItemsObject"
755
981
756withFieldSchema :: forall proxy s f. (Selector s, GToSchema f) =>
982withFieldSchema :: forall proxy s f. (Selector s, GToSchema f) =>
757 SchemaOptions -> proxy s f -> Bool -> Schema -> Declare (Definitions Schema) Schema
983 SchemaOptions -> proxy s f -> Bool -> Schema -> Declare (Definitions Schema) Schema
@@ -760,12 +986,12 @@ withFieldSchema opts _ isRequiredField schema = do
760 return $
986 return $
761 if T.null fname
987 if T.null fname
762 then schema
988 then schema
763− & type_ ?~ SwaggerArray
989+ & type_ ?~ OpenApiArray
764 & items %~ appendItem ref
990 & items %~ appendItem ref
765 & maxItems %~ Just . maybe 1 (+1) -- increment maxItems
991 & maxItems %~ Just . maybe 1 (+1) -- increment maxItems
766 & minItems %~ Just . maybe 1 (+1) -- increment minItems
992 & minItems %~ Just . maybe 1 (+1) -- increment minItems
767 else schema
993 else schema
768− & type_ ?~ SwaggerObject
994+ & type_ ?~ OpenApiObject
769 & properties . at fname ?~ ref
995 & properties . at fname ?~ ref
770 & if isRequiredField
996 & if isRequiredField
771 then required %~ (++ [fname])
997 then required %~ (++ [fname])
@@ -791,62 +1017,110 @@ instance ( GSumToSchema f
791 , GSumToSchema g
1017 , GSumToSchema g
792 ) => GToSchema (f :+: g)
1018 ) => GToSchema (f :+: g)
793 where
1019 where
1020+ -- Aeson does not unwrap unary record in sum types.
794− gdeclareNamedSchema = gdeclareNamedSumSchema
1021+ gdeclareNamedSchema opts p s = gdeclareNamedSumSchema (opts { unwrapUnaryRecords = False } )p s
795
1022
796gdeclareNamedSumSchema :: GSumToSchema f => SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
1023gdeclareNamedSumSchema :: GSumToSchema f => SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
797−gdeclareNamedSumSchema opts proxy s
1024+gdeclareNamedSumSchema opts proxy _
798− | allNullaryToStringTag opts && allNullary = pure $ unnamed (toStringTag sumSchema)
1025+ | allNullaryToStringTag opts && allNullary = pure $ unnamed (toStringTag sumSchemas)
799− | otherwise = (unnamed . fst) <$> runWriterT declareSumSchema
1026+ | otherwise = do
1027+ (schemas, _) <- runWriterT declareSumSchema
1028+ return $ unnamed $ mempty
1029+ & type_ ?~ OpenApiObject
1030+ & oneOf ?~ (snd <$> schemas)
800 where
1031 where
801− declareSumSchema = gsumToSchema opts proxy s
1032+ declareSumSchema = gsumToSchema opts proxy
802− (sumSchema, All allNullary) = undeclare (runWriterT declareSumSchema)
1033+ (sumSchemas, All allNullary) = undeclare (runWriterT declareSumSchema)
803
1034
804− toStringTag schema = mempty
1035+ toStringTag schemas = mempty
805− & type_ ?~ SwaggerString
1036+ & type_ ?~ OpenApiString
806− & enum_ ?~ map toJSON (schema ^.. properties.ifolded.asIndex)
1037+ & enum_ ?~ map (String . fst) sumSchemas
807
1038
808type AllNullary = All
1039type AllNullary = All
809
1040
810class GSumToSchema (f :: * -> *) where
1041class GSumToSchema (f :: * -> *) where
811− gsumToSchema :: SchemaOptions -> Proxy f -> Schema -> WriterT AllNullary (Declare (Definitions Schema)) Schema
1042+ gsumToSchema :: SchemaOptions -> Proxy f -> WriterT AllNullary (Declare (Definitions Schema)) [(T.Text, Referenced Schema)]
812
1043
813instance (GSumToSchema f, GSumToSchema g) => GSumToSchema (f :+: g) where
1044instance (GSumToSchema f, GSumToSchema g) => GSumToSchema (f :+: g) where
1045+ gsumToSchema opts _ =
814− gsumToSchema opts _ = gsumToSchema opts (Proxy :: Proxy f) >=> gsumToSchema opts (Proxy :: Proxy g)
1046+ (<>) <$> gsumToSchema opts (Proxy :: Proxy f) <*> gsumToSchema opts (Proxy :: Proxy g)
815
1047
1048+-- | Convert one component of the sum to schema, to be later combined with @oneOf@.
816gsumConToSchemaWith :: forall c f. (GToSchema (C1 c f), Constructor c) =>
1049gsumConToSchemaWith :: forall c f. (GToSchema (C1 c f), Constructor c) =>
817− Referenced Schema -> SchemaOptions -> Proxy (C1 c f) -> Schema -> Schema
1050+ Maybe (Referenced Schema) -> SchemaOptions -> Proxy (C1 c f) -> (T.Text, Referenced Schema)
818−gsumConToSchemaWith ref opts _ schema = schema
1051+gsumConToSchemaWith ref opts _ = (tag, schema)
819− & type_ ?~ SwaggerObject
820− & properties . at tag ?~ ref
821− & maxProperties ?~ 1
822− & minProperties ?~ 1
823 where
1052 where
1053+ schema = case sumEncoding opts of
1054+ TaggedObject tagField contentsField ->
1055+ case ref of
1056+ -- If subschema is an object and constructor is a record, we add tag directly
1057+ -- to the record, as Aeson does it.
1058+ Just (Inline sub) | sub ^. type_ == Just OpenApiObject && isRecord -> Inline $ sub
1059+ & required <>~ [T.pack tagField]
1060+ & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag])
1061+
1062+ -- If it is not a record, we need to put subschema into "contents" field.
1063+ _ | not isRecord -> Inline $ mempty
1064+ & type_ ?~ OpenApiObject
1065+ & required .~ [T.pack tagField]
1066+ & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag])
1067+ -- If constructor is nullary, there is no content.
1068+ & case ref of
1069+ Just r -> (properties . at (T.pack contentsField) ?~ r) . (required <>~ [T.pack contentsField])
1070+ Nothing -> id
1071+
1072+ -- In the remaining cases we combine "tag" object and "contents" object using allOf.
1073+ _ -> Inline $ mempty
1074+ & type_ ?~ OpenApiObject
1075+ & allOf ?~ [Inline $ mempty
1076+ & type_ ?~ OpenApiObject
1077+ & required .~ (T.pack tagField : if isRecord then [] else [T.pack contentsField])
1078+ & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag])]
1079+ & if isRecord
1080+ then allOf . _Just <>~ [refOrNullary]
1081+ else allOf . _Just <>~ [Inline $ mempty & type_ ?~ OpenApiObject & properties . at (T.pack contentsField) ?~ refOrNullary]
1082+ UntaggedValue -> refOrEnum -- Aeson encodes nullary constructors as strings in this case.
1083+ ObjectWithSingleField -> Inline $ mempty
1084+ & type_ ?~ OpenApiObject
1085+ & required .~ [tag]
1086+ & properties . at tag ?~ refOrNullary
1087+ TwoElemArray -> error "unrepresentable in OpenAPI 3"
1088+
824 tag = T.pack (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
1089 tag = T.pack (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
1090+ isRecord = conIsRecord (Proxy3 :: Proxy3 c f p)
1091+ refOrNullary = fromMaybe (Inline nullarySchema) ref
1092+ refOrEnum = fromMaybe (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag]) ref
825
1093
826gsumConToSchema :: (GToSchema (C1 c f), Constructor c) =>
1094gsumConToSchema :: (GToSchema (C1 c f), Constructor c) =>
827− SchemaOptions -> Proxy (C1 c f) -> Schema -> Declare (Definitions Schema) Schema
1095+ SchemaOptions -> Proxy (C1 c f) -> Declare (Definitions Schema) [(T.Text, Referenced Schema)]
828−gsumConToSchema opts proxy schema = do
1096+gsumConToSchema opts proxy = do
829 ref <- gdeclareSchemaRef opts proxy
1097 ref <- gdeclareSchemaRef opts proxy
830− return $ gsumConToSchemaWith ref opts proxy schema
1098+ return [gsumConToSchemaWith (Just ref) opts proxy]
831
1099
832instance {-# OVERLAPPABLE #-} (Constructor c, GToSchema f) => GSumToSchema (C1 c f) where
1100instance {-# OVERLAPPABLE #-} (Constructor c, GToSchema f) => GSumToSchema (C1 c f) where
833− gsumToSchema opts proxy schema = do
1101+ gsumToSchema opts proxy = do
834 tell (All False)
1102 tell (All False)
835− lift $ gsumConToSchema opts proxy schema
1103+ lift $ gsumConToSchema opts proxy
836
1104
837instance (Constructor c, Selector s, GToSchema f) => GSumToSchema (C1 c (S1 s f)) where
1105instance (Constructor c, Selector s, GToSchema f) => GSumToSchema (C1 c (S1 s f)) where
838− gsumToSchema opts proxy schema = do
1106+ gsumToSchema opts proxy = do
839 tell (All False)
1107 tell (All False)
840− lift $ gsumConToSchema opts proxy schema
1108+ lift $ gsumConToSchema opts proxy
841
1109
842instance Constructor c => GSumToSchema (C1 c U1) where
1110instance Constructor c => GSumToSchema (C1 c U1) where
843− gsumToSchema opts proxy = pure . gsumConToSchemaWith (Inline nullarySchema) opts proxy
1111+ gsumToSchema opts proxy = pure $ (:[]) $ gsumConToSchemaWith Nothing opts proxy
844
1112
845data Proxy2 a b = Proxy2
1113data Proxy2 a b = Proxy2
846
1114
847data Proxy3 a b c = Proxy3
1115data Proxy3 a b c = Proxy3
848
1116
849−-- $setup
1117+{- $setup
850−-- >>> import Data.Swagger
1118+>>> import Data.OpenApi
851−-- >>> import Data.Aeson (encode)
1119+>>> import Data.Aeson (encode)
852−-- >>> import Data.Aeson.Types (toJSONKeyText)
1120+>>> import Data.Aeson.Types (toJSONKeyText)
1121+>>> import Data.OpenApi.Internal.Utils
1122+>>> :set -XScopedTypeVariables
1123+>>> :set -XDeriveAnyClass
1124+>>> :set -XStandaloneDeriving
1125+>>> :set -XTypeApplications
1126+-}
src/Data/Swagger/Internal/Schema/Validation.hs → src/Data/OpenApi/Internal/Schema/Validation.hsrenamed+119−98
@@ -1,4 +1,5 @@
1{-# OPTIONS_GHC -Wall #-}
1{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
@@ -12,29 +13,33 @@
12{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE TemplateHaskell #-}
14{-# LANGUAGE TemplateHaskell #-}
14{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
16+{-# LANGUAGE ViewPatterns #-}
15-- |
17-- |
16−-- Module: Data.Swagger.Internal.Schema.Validation
18+-- Module: Data.OpenApi.Internal.Schema.Validation
17-- Copyright: (c) 2015 GetShopTV
19-- Copyright: (c) 2015 GetShopTV
18-- License: BSD3
20-- License: BSD3
19-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
21-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
20-- Stability: experimental
22-- Stability: experimental
21--
23--
22-- Validate JSON values with Swagger Schema.
24-- Validate JSON values with Swagger Schema.
23−module Data.Swagger.Internal.Schema.Validation where
25+module Data.OpenApi.Internal.Schema.Validation where
24
26
25import Prelude ()
27import Prelude ()
26import Prelude.Compat
28import Prelude.Compat
27
29
28import Control.Applicative
30import Control.Applicative
29−import Control.Lens
31+import Control.Lens hiding (allOf)
30−import Control.Monad (when)
32+import Control.Monad (forM, forM_, when)
31
33
32import Data.Aeson hiding (Result)
34import Data.Aeson hiding (Result)
33−import Data.Aeson.Encode.Pretty (encodePretty)
35+#if MIN_VERSION_aeson(2,0,0)
36+import qualified Data.Aeson.KeyMap as KeyMap
37+#endif
34import Data.Foldable (for_, sequenceA_,
38import Data.Foldable (for_, sequenceA_,
35 traverse_)
39 traverse_)
40+#if !MIN_VERSION_aeson(2,0,0)
36import Data.HashMap.Strict (HashMap)
41import Data.HashMap.Strict (HashMap)
37−import qualified Data.HashMap.Strict as HashMap
42+#endif
38import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
43import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
39import qualified "unordered-containers" Data.HashSet as HashSet
44import qualified "unordered-containers" Data.HashSet as HashSet
40import Data.Maybe (fromMaybe)
45import Data.Maybe (fromMaybe)
@@ -47,10 +52,12 @@ import qualified Data.Text.Lazy.Encoding as TL
47import Data.Vector (Vector)
52import Data.Vector (Vector)
48import qualified Data.Vector as Vector
53import qualified Data.Vector as Vector
49
54
50−import Data.Swagger.Declare
55+import Data.OpenApi.Aeson.Compat (hasKey, keyToText, lookupKey, objectToList)
56+import Data.OpenApi.Declare
51−import Data.Swagger.Internal
57+import Data.OpenApi.Internal
52−import Data.Swagger.Internal.Schema
58+import Data.OpenApi.Internal.Schema
53−import Data.Swagger.Lens
59+import Data.OpenApi.Internal.Utils
60+import Data.OpenApi.Lens
54
61
55-- | Validate @'ToJSON'@ instance matches @'ToSchema'@ for a given value.
62-- | Validate @'ToJSON'@ instance matches @'ToSchema'@ for a given value.
56-- This can be used with QuickCheck to ensure those instances are coherent:
63-- This can be used with QuickCheck to ensure those instances are coherent:
@@ -102,33 +109,33 @@ validateToJSONWithPatternChecker checker = validateJSONWithPatternChecker checke
102-- <BLANKLINE>
109-- <BLANKLINE>
103-- Swagger Schema:
110-- Swagger Schema:
104-- {
111-- {
105−-- "required": [
106−-- "name",
107−-- "phone"
108−-- ],
109−-- "type": "object",
110-- "properties": {
112-- "properties": {
111−-- "phone": {
112−-- "$ref": "#/definitions/Phone"
113−-- },
114-- "name": {
113-- "name": {
115-- "type": "string"
114-- "type": "string"
115+-- },
116+-- "phone": {
117+-- "$ref": "#/components/schemas/Phone"
116-- }
118-- }
117−-- }
119+-- },
120+-- "required": [
121+-- "name",
122+-- "phone"
123+-- ],
124+-- "type": "object"
118-- }
125-- }
119-- <BLANKLINE>
126-- <BLANKLINE>
120-- Swagger Description Context:
127-- Swagger Description Context:
121-- {
128-- {
122-- "Phone": {
129-- "Phone": {
123−-- "required": [
124−-- "value"
125−-- ],
126−-- "type": "object",
127-- "properties": {
130-- "properties": {
128-- "value": {
131-- "value": {
129-- "type": "string"
132-- "type": "string"
130-- }
133-- }
131−-- }
134+-- },
135+-- "required": [
136+-- "value"
137+-- ],
138+-- "type": "object"
132-- }
139-- }
133-- }
140-- }
134-- <BLANKLINE>
141-- <BLANKLINE>
@@ -204,7 +211,7 @@ instance Monad Result where
204
211
205-- | Validation configuration.
212-- | Validation configuration.
206data Config = Config
213data Config = Config
207− { -- | Pattern checker for @'_paramSchemaPattern'@ validation.
214+ { -- | Pattern checker for @'_schemaPattern'@ validation.
208 configPatternChecker :: Pattern -> Text -> Bool
215 configPatternChecker :: Pattern -> Text -> Bool
209 -- | Schema definitions in scope to resolve references.
216 -- | Schema definitions in scope to resolve references.
210 , configDefinitions :: Definitions Schema
217 , configDefinitions :: Definitions Schema
@@ -297,40 +304,34 @@ validateWithSchemaRef (Inline s) js = sub s (validateWithSchema js)
297
304
298-- | Validate JSON @'Value'@ with Swagger @'Schema'@.
305-- | Validate JSON @'Value'@ with Swagger @'Schema'@.
299validateWithSchema :: Value -> Validation Schema ()
306validateWithSchema :: Value -> Validation Schema ()
300−validateWithSchema value = do
307+validateWithSchema val = do
301− validateSchemaType value
308+ validateSchemaType val
302− sub_ paramSchema $ validateEnum value
303−
304−-- | Validate JSON @'Value'@ with Swagger @'ParamSchema'@.
305−validateWithParamSchema :: Value -> Validation (ParamSchema t) ()
306−validateWithParamSchema value = do
307− validateParamSchemaType value
308− validateEnum value
309+ validateEnum val
309
310
310−validateInteger :: Scientific -> Validation (ParamSchema t) ()
311+validateInteger :: Scientific -> Validation Schema ()
311validateInteger n = do
312validateInteger n = do
312 when (not (isInteger n)) $
313 when (not (isInteger n)) $
313 invalid ("not an integer")
314 invalid ("not an integer")
314 validateNumber n
315 validateNumber n
315
316
316−validateNumber :: Scientific -> Validation (ParamSchema t) ()
317+validateNumber :: Scientific -> Validation Schema ()
317validateNumber n = withConfig $ \_cfg -> withSchema $ \sch -> do
318validateNumber n = withConfig $ \_cfg -> withSchema $ \sch -> do
318 let exMax = Just True == sch ^. exclusiveMaximum
319 let exMax = Just True == sch ^. exclusiveMaximum
319 exMin = Just True == sch ^. exclusiveMinimum
320 exMin = Just True == sch ^. exclusiveMinimum
320
321
321 check maximum_ $ \m ->
322 check maximum_ $ \m ->
322 when (if exMax then (n >= m) else (n > m)) $
323 when (if exMax then (n >= m) else (n > m)) $
323− invalid ("value " ++ show n ++ " exceeds maximum (should be " ++ if exMax then "<" else "<=" ++ show m ++ ")")
324+ invalid ("value " ++ show n ++ " exceeds maximum (should be " ++ (if exMax then "<" else "<=") ++ show m ++ ")")
324
325
325 check minimum_ $ \m ->
326 check minimum_ $ \m ->
326 when (if exMin then (n <= m) else (n < m)) $
327 when (if exMin then (n <= m) else (n < m)) $
327− invalid ("value " ++ show n ++ " falls below minimum (should be " ++ if exMin then ">" else ">=" ++ show m ++ ")")
328+ invalid ("value " ++ show n ++ " falls below minimum (should be " ++ (if exMin then ">" else ">=") ++ show m ++ ")")
328
329
329 check multipleOf $ \k ->
330 check multipleOf $ \k ->
330 when (not (isInteger (n / k))) $
331 when (not (isInteger (n / k))) $
331 invalid ("expected a multiple of " ++ show k ++ " but got " ++ show n)
332 invalid ("expected a multiple of " ++ show k ++ " but got " ++ show n)
332
333
333−validateString :: Text -> Validation (ParamSchema t) ()
334+validateString :: Text -> Validation Schema ()
334validateString s = do
335validateString s = do
335 check maxLength $ \n ->
336 check maxLength $ \n ->
336 when (len > fromInteger n) $
337 when (len > fromInteger n) $
@@ -347,7 +348,7 @@ validateString s = do
347 where
348 where
348 len = Text.length s
349 len = Text.length s
349
350
350−validateArray :: Vector Value -> Validation (ParamSchema t) ()
351+validateArray :: Vector Value -> Validation Schema ()
351validateArray xs = do
352validateArray xs = do
352 check maxItems $ \n ->
353 check maxItems $ \n ->
353 when (len > fromInteger n) $
354 when (len > fromInteger n) $
@@ -358,9 +359,8 @@ validateArray xs = do
358 invalid ("array is too short (size should be >=" ++ show n ++ ")")
359 invalid ("array is too short (size should be >=" ++ show n ++ ")")
359
360
360 check items $ \case
361 check items $ \case
361− SwaggerItemsPrimitive _ itemSchema -> sub itemSchema $ traverse_ validateWithParamSchema xs
362− SwaggerItemsObject itemSchema -> traverse_ (validateWithSchemaRef itemSchema) xs
362+ OpenApiItemsObject itemSchema -> traverse_ (validateWithSchemaRef itemSchema) xs
363− SwaggerItemsArray itemSchemas -> do
363+ OpenApiItemsArray itemSchemas -> do
364 when (len /= length itemSchemas) $
364 when (len /= length itemSchemas) $
365 invalid ("array size is invalid (should be exactly " ++ show (length itemSchemas) ++ ")")
365 invalid ("array size is invalid (should be exactly " ++ show (length itemSchemas) ++ ")")
366 sequenceA_ (zipWith validateWithSchemaRef itemSchemas (Vector.toList xs))
366 sequenceA_ (zipWith validateWithSchemaRef itemSchemas (Vector.toList xs))
@@ -372,10 +372,16 @@ validateArray xs = do
372 len = Vector.length xs
372 len = Vector.length xs
373 allUnique = len == HashSet.size (HashSet.fromList (Vector.toList xs))
373 allUnique = len == HashSet.size (HashSet.fromList (Vector.toList xs))
374
374
375+validateObject ::
376+#if MIN_VERSION_aeson(2,0,0)
377+ KeyMap.KeyMap Value
378+#else
375−validateObject :: HashMap Text Value -> Validation Schema ()
379+ HashMap Text Value
380+#endif
381+ -> Validation Schema ()
376validateObject o = withSchema $ \sch ->
382validateObject o = withSchema $ \sch ->
377 case sch ^. discriminator of
383 case sch ^. discriminator of
378− Just (Discriminator pname types) -> case fromJSON <$> HashMap.lookup pname o of
384+ Just (Discriminator pname types) -> case fromJSON <$> lookupKey pname o of
379 Just (Success pvalue) ->
385 Just (Success pvalue) ->
380 let ref = fromMaybe pvalue $ InsOrdHashMap.lookup pvalue types
386 let ref = fromMaybe pvalue $ InsOrdHashMap.lookup pvalue types
381 -- TODO ref may be name or reference
387 -- TODO ref may be name or reference
@@ -394,15 +400,15 @@ validateObject o = withSchema $ \sch ->
394 validateRequired
400 validateRequired
395 validateProps
401 validateProps
396 where
402 where
397− size = fromIntegral (HashMap.size o)
403+ size = fromIntegral (length o)
398
404
399 validateRequired = withSchema $ \sch -> traverse_ validateReq (sch ^. required)
405 validateRequired = withSchema $ \sch -> traverse_ validateReq (sch ^. required)
400 validateReq n =
406 validateReq n =
401− when (not (HashMap.member n o)) $
407+ when (not (hasKey n o)) $
402 invalid ("property " ++ show n ++ " is required, but not found in " ++ show (encode o))
408 invalid ("property " ++ show n ++ " is required, but not found in " ++ show (encode o))
403
409
404 validateProps = withSchema $ \sch -> do
410 validateProps = withSchema $ \sch -> do
405− for_ (HashMap.toList o) $ \(k, v) ->
411+ for_ (objectToList o) $ \(keyToText -> k, v) ->
406 case v of
412 case v of
407 Null | not (k `elem` (sch ^. required)) -> valid -- null is fine for non-required property
413 Null | not (k `elem` (sch ^. required)) -> valid -- null is fine for non-required property
408 _ ->
414 _ ->
@@ -418,21 +424,21 @@ validateObject o = withSchema $ \sch ->
418 unknownProperty pname = invalid $
424 unknownProperty pname = invalid $
419 "property " <> show pname <> " is found in JSON value, but it is not mentioned in Swagger schema"
425 "property " <> show pname <> " is found in JSON value, but it is not mentioned in Swagger schema"
420
426
421−validateEnum :: Value -> Validation (ParamSchema t) ()
427+validateEnum :: Value -> Validation Schema ()
422−validateEnum value = do
428+validateEnum val = do
423 check enum_ $ \xs ->
429 check enum_ $ \xs ->
424− when (value `notElem` xs) $
430+ when (val `notElem` xs) $
425− invalid ("expected one of " ++ show (encode xs) ++ " but got " ++ show value)
431+ invalid ("expected one of " ++ show (encode xs) ++ " but got " ++ show val)
426
432
427-- | Infer schema type based on used properties.
433-- | Infer schema type based on used properties.
428--
434--
429-- This is like 'inferParamSchemaTypes', but also works for objects:
435-- This is like 'inferParamSchemaTypes', but also works for objects:
430--
436--
431-- >>> inferSchemaTypes <$> decode "{\"minProperties\": 1}"
437-- >>> inferSchemaTypes <$> decode "{\"minProperties\": 1}"
432−-- Just [SwaggerObject]
438+-- Just [OpenApiObject]
433−inferSchemaTypes :: Schema -> [SwaggerType 'SwaggerKindSchema]
439+inferSchemaTypes :: Schema -> [OpenApiType]
434−inferSchemaTypes sch = inferParamSchemaTypes (sch ^. paramSchema) ++
440+inferSchemaTypes sch = inferParamSchemaTypes sch ++
435− [ SwaggerObject | any ($ sch)
441+ [ OpenApiObject | any ($ sch)
436 [ has (additionalProperties._Just)
442 [ has (additionalProperties._Just)
437 , has (maxProperties._Just)
443 , has (maxProperties._Just)
438 , has (minProperties._Just)
444 , has (minProperties._Just)
@@ -442,62 +448,77 @@ inferSchemaTypes sch = inferParamSchemaTypes (sch ^. paramSchema) ++
442-- | Infer schema type based on used properties.
448-- | Infer schema type based on used properties.
443--
449--
444-- >>> inferSchemaTypes <$> decode "{\"minLength\": 2}"
450-- >>> inferSchemaTypes <$> decode "{\"minLength\": 2}"
445−-- Just [SwaggerString]
451+-- Just [OpenApiString]
446--
452--
447-- >>> inferSchemaTypes <$> decode "{\"maxItems\": 0}"
453-- >>> inferSchemaTypes <$> decode "{\"maxItems\": 0}"
448−-- Just [SwaggerArray]
454+-- Just [OpenApiArray]
449--
455--
450−-- From numeric properties 'SwaggerInteger' type is inferred.
456+-- From numeric properties 'OpenApiInteger' type is inferred.
451−-- If you want 'SwaggerNumber' instead, you must specify it explicitly.
457+-- If you want 'OpenApiNumber' instead, you must specify it explicitly.
452--
458--
453-- >>> inferSchemaTypes <$> decode "{\"minimum\": 1}"
459-- >>> inferSchemaTypes <$> decode "{\"minimum\": 1}"
454−-- Just [SwaggerInteger]
460+-- Just [OpenApiInteger]
455−inferParamSchemaTypes :: ParamSchema t -> [SwaggerType t]
461+inferParamSchemaTypes :: Schema -> [OpenApiType]
456inferParamSchemaTypes sch = concat
462inferParamSchemaTypes sch = concat
457− [ [ SwaggerArray | any ($ sch)
463+ [ [ OpenApiArray | any ($ sch)
458 [ has (items._Just)
464 [ has (items._Just)
459 , has (maxItems._Just)
465 , has (maxItems._Just)
460 , has (minItems._Just)
466 , has (minItems._Just)
461 , has (uniqueItems._Just) ] ]
467 , has (uniqueItems._Just) ] ]
462− , [ SwaggerInteger | any ($ sch)
468+ , [ OpenApiInteger | any ($ sch)
463 [ has (exclusiveMaximum._Just)
469 [ has (exclusiveMaximum._Just)
464 , has (exclusiveMinimum._Just)
470 , has (exclusiveMinimum._Just)
465 , has (maximum_._Just)
471 , has (maximum_._Just)
466 , has (minimum_._Just)
472 , has (minimum_._Just)
467 , has (multipleOf._Just) ] ]
473 , has (multipleOf._Just) ] ]
468− , [ SwaggerString | any ($ sch)
474+ , [ OpenApiString | any ($ sch)
469 [ has (maxLength._Just)
475 [ has (maxLength._Just)
470 , has (minLength._Just)
476 , has (minLength._Just)
471 , has (pattern._Just) ] ]
477 , has (pattern._Just) ] ]
472 ]
478 ]
473
479
474validateSchemaType :: Value -> Validation Schema ()
480validateSchemaType :: Value -> Validation Schema ()
475−validateSchemaType value = withSchema $ \sch ->
481+validateSchemaType val = withSchema $ \sch ->
482+ case sch of
483+ (view oneOf -> Just variants) -> do
484+ res <- forM variants $ \var ->
485+ (True <$ validateWithSchemaRef var val) <|> (return False)
486+ case length $ filter id res of
487+ 0 -> invalid $ "Value not valid under any of 'oneOf' schemas: " ++ show val
488+ 1 -> valid
489+ _ -> invalid $ "Value matches more than one of 'oneOf' schemas: " ++ show val
490+ (view allOf -> Just variants) -> do
491+ -- Default semantics for Validation Monad will abort when at least one
492+ -- variant does not match.
493+ forM_ variants $ \var ->
494+ validateWithSchemaRef var val
495+
496+ _ ->
476− case (sch ^. type_, value) of
497+ case (sch ^. type_, val) of
477− (Just SwaggerNull, Null) -> valid
498+ (Just OpenApiNull, Null) -> valid
478− (Just SwaggerBoolean, Bool _) -> valid
499+ (Just OpenApiBoolean, Bool _) -> valid
479− (Just SwaggerInteger, Number n) -> sub_ paramSchema (validateInteger n)
500+ (Just OpenApiInteger, Number n) -> validateInteger n
480− (Just SwaggerNumber, Number n) -> sub_ paramSchema (validateNumber n)
501+ (Just OpenApiNumber, Number n) -> validateNumber n
481− (Just SwaggerString, String s) -> sub_ paramSchema (validateString s)
502+ (Just OpenApiString, String s) -> validateString s
482− (Just SwaggerArray, Array xs) -> sub_ paramSchema (validateArray xs)
503+ (Just OpenApiArray, Array xs) -> validateArray xs
483− (Just SwaggerObject, Object o) -> validateObject o
504+ (Just OpenApiObject, Object o) -> validateObject o
484− (Nothing, Null) -> valid
505+ (Nothing, Null) -> valid
485− (Nothing, Bool _) -> valid
506+ (Nothing, Bool _) -> valid
486− -- Number by default
507+ -- Number by default
487− (Nothing, Number n) -> sub_ paramSchema (validateNumber n)
508+ (Nothing, Number n) -> validateNumber n
488− (Nothing, String s) -> sub_ paramSchema (validateString s)
509+ (Nothing, String s) -> validateString s
489− (Nothing, Array xs) -> sub_ paramSchema (validateArray xs)
510+ (Nothing, Array xs) -> validateArray xs
490− (Nothing, Object o) -> validateObject o
511+ (Nothing, Object o) -> validateObject o
491− bad -> invalid $ "expected JSON value of type " ++ showType bad
512+ bad -> invalid $ "expected JSON value of type " ++ showType bad
492−
513+
493−validateParamSchemaType :: Value -> Validation (ParamSchema t) ()
514+validateParamSchemaType :: Value -> Validation Schema ()
494−validateParamSchemaType value = withSchema $ \sch ->
515+validateParamSchemaType val = withSchema $ \sch ->
495− case (sch ^. type_, value) of
516+ case (sch ^. type_, val) of
496− (Just SwaggerBoolean, Bool _) -> valid
517+ (Just OpenApiBoolean, Bool _) -> valid
497− (Just SwaggerInteger, Number n) -> validateInteger n
518+ (Just OpenApiInteger, Number n) -> validateInteger n
498− (Just SwaggerNumber, Number n) -> validateNumber n
519+ (Just OpenApiNumber, Number n) -> validateNumber n
499− (Just SwaggerString, String s) -> validateString s
520+ (Just OpenApiString, String s) -> validateString s
500− (Just SwaggerArray, Array xs) -> validateArray xs
521+ (Just OpenApiArray, Array xs) -> validateArray xs
501 (Nothing, Bool _) -> valid
522 (Nothing, Bool _) -> valid
502 -- Number by default
523 -- Number by default
503 (Nothing, Number n) -> validateNumber n
524 (Nothing, Number n) -> validateNumber n
@@ -505,11 +526,11 @@ validateParamSchemaType value = withSchema $ \sch ->
505 (Nothing, Array xs) -> validateArray xs
526 (Nothing, Array xs) -> validateArray xs
506 bad -> invalid $ "expected JSON value of type " ++ showType bad
527 bad -> invalid $ "expected JSON value of type " ++ showType bad
507
528
508−showType :: (Maybe (SwaggerType t), Value) -> String
529+showType :: (Maybe OpenApiType, Value) -> String
509showType (Just ty, _) = show ty
530showType (Just ty, _) = show ty
510−showType (Nothing, Null) = "SwaggerNull"
531+showType (Nothing, Null) = "OpenApiNull"
511−showType (Nothing, Bool _) = "SwaggerBoolean"
532+showType (Nothing, Bool _) = "OpenApiBoolean"
512−showType (Nothing, Number _) = "SwaggerNumber"
533+showType (Nothing, Number _) = "OpenApiNumber"
513−showType (Nothing, String _) = "SwaggerString"
534+showType (Nothing, String _) = "OpenApiString"
514−showType (Nothing, Array _) = "SwaggerArray"
535+showType (Nothing, Array _) = "OpenApiArray"
515−showType (Nothing, Object _) = "SwaggerObject"
536+showType (Nothing, Object _) = "OpenApiObject"
src/Data/Swagger/Internal/TypeShape.hs → src/Data/OpenApi/Internal/TypeShape.hsrenamed+1−1
@@ -4,7 +4,7 @@
4{-# LANGUAGE TypeOperators #-}
4{-# LANGUAGE TypeOperators #-}
5{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
6
6
7−module Data.Swagger.Internal.TypeShape where
7+module Data.OpenApi.Internal.TypeShape where
8
8
9import Data.Proxy
9import Data.Proxy
10import GHC.Generics
10import GHC.Generics
src/Data/Swagger/Internal/Utils.hs → src/Data/OpenApi/Internal/Utils.hsrenamed+6−1
@@ -4,7 +4,7 @@
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE TypeOperators #-}
6{-# LANGUAGE TypeOperators #-}
7−module Data.Swagger.Internal.Utils where
7+module Data.OpenApi.Internal.Utils where
8
8
9import Prelude ()
9import Prelude ()
10import Prelude.Compat
10import Prelude.Compat
@@ -13,6 +13,8 @@ import Control.Lens ((&), (%~))
13import Control.Lens.TH
13import Control.Lens.TH
14import Data.Aeson
14import Data.Aeson
15import Data.Aeson.Types
15import Data.Aeson.Types
16+import qualified Data.Aeson.Encode.Pretty as P
17+import qualified Data.ByteString.Lazy as BSL
16import Data.Char
18import Data.Char
17import Data.Data
19import Data.Data
18import Data.Hashable (Hashable)
20import Data.Hashable (Hashable)
@@ -132,3 +134,6 @@ instance SwaggerMonoid (Maybe a) where
132 swaggerMempty = Nothing
134 swaggerMempty = Nothing
133 swaggerMappend x Nothing = x
135 swaggerMappend x Nothing = x
134 swaggerMappend _ y = y
136 swaggerMappend _ y = y
137+
138+encodePretty :: ToJSON a => a -> BSL.ByteString
139+encodePretty = P.encodePretty' $ P.defConfig { P.confCompare = P.compare }
src/Data/OpenApi/Lens.hsnew file+166
@@ -0,0 +1,166 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE DataKinds #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+{-# OPTIONS_GHC -fno-warn-orphans #-}
12+-- |
13+-- Module: Data.OpenApi.Lens
14+-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
15+-- Stability: experimental
16+--
17+-- Lenses and prisms for Swagger.
18+module Data.OpenApi.Lens where
19+
20+import Control.Lens
21+import Data.Aeson (Value)
22+import Data.Scientific (Scientific)
23+import Data.OpenApi.Internal
24+import Data.OpenApi.Internal.Utils
25+import Data.Text (Text)
26+
27+-- * Classy lenses
28+
29+makeFields ''OpenApi
30+makeFields ''Components
31+makeFields ''Server
32+makeFields ''RequestBody
33+makeFields ''MediaTypeObject
34+makeFields ''Info
35+makeFields ''Contact
36+makeFields ''License
37+makeLensesWith swaggerFieldRules ''PathItem
38+makeFields ''Tag
39+makeFields ''Operation
40+makeLensesWith swaggerFieldRules ''Param
41+makeFields ''Header
42+makeLensesWith swaggerFieldRules ''Schema
43+makeFields ''NamedSchema
44+makeFields ''Xml
45+makeLensesWith swaggerFieldRules ''Responses
46+makeFields ''Response
47+makeLensesWith swaggerFieldRules ''SecurityScheme
48+makeFields ''ApiKeyParams
49+makeFields ''OAuth2ImplicitFlow
50+makeFields ''OAuth2PasswordFlow
51+makeFields ''OAuth2ClientCredentialsFlow
52+makeFields ''OAuth2AuthorizationCodeFlow
53+makeFields ''OAuth2Flow
54+makeFields ''OAuth2Flows
55+makeFields ''ExternalDocs
56+makeFields ''Encoding
57+makeFields ''Example
58+makeFields ''Discriminator
59+makeFields ''Link
60+
61+-- * Prisms
62+-- ** 'SecuritySchemeType' prisms
63+makePrisms ''SecuritySchemeType
64+-- ** 'Referenced' prisms
65+makePrisms ''Referenced
66+
67+-- ** 'OpenApiItems' prisms
68+
69+_OpenApiItemsArray :: Review OpenApiItems [Referenced Schema]
70+_OpenApiItemsArray
71+ = unto (\x -> OpenApiItemsArray x)
72+{- \x -> case x of
73+ OpenApiItemsPrimitive c p -> Left (OpenApiItemsPrimitive c p)
74+ OpenApiItemsObject o -> Left (OpenApiItemsObject o)
75+ OpenApiItemsArray a -> Right a
76+-}
77+
78+_OpenApiItemsObject :: Review OpenApiItems (Referenced Schema)
79+_OpenApiItemsObject
80+ = unto (\x -> OpenApiItemsObject x)
81+{- \x -> case x of
82+ OpenApiItemsPrimitive c p -> Left (OpenApiItemsPrimitive c p)
83+ OpenApiItemsObject o -> Right o
84+ OpenApiItemsArray a -> Left (OpenApiItemsArray a)
85+-}
86+
87+-- =============================================================
88+-- More helpful instances for easier access to schema properties
89+
90+type instance Index Responses = HttpStatusCode
91+type instance Index Operation = HttpStatusCode
92+
93+type instance IxValue Responses = Referenced Response
94+type instance IxValue Operation = Referenced Response
95+
96+instance Ixed Responses where ix n = responses . ix n
97+instance At Responses where at n = responses . at n
98+
99+instance Ixed Operation where ix n = responses . ix n
100+instance At Operation where at n = responses . at n
101+
102+instance HasType NamedSchema (Maybe OpenApiType) where type_ = schema.type_
103+
104+-- OVERLAPPABLE instances
105+
106+instance
107+ {-# OVERLAPPABLE #-}
108+ HasSchema s Schema
109+ => HasFormat s (Maybe Format) where
110+ format = schema.format
111+
112+instance
113+ {-# OVERLAPPABLE #-}
114+ HasSchema s Schema
115+ => HasItems s (Maybe OpenApiItems) where
116+ items = schema.items
117+
118+instance
119+ {-# OVERLAPPABLE #-}
120+ HasSchema s Schema
121+ => HasMaximum s (Maybe Scientific) where
122+ maximum_ = schema.maximum_
123+
124+instance {-# OVERLAPPABLE #-} HasSchema s Schema
125+ => HasExclusiveMaximum s (Maybe Bool) where
126+ exclusiveMaximum = schema.exclusiveMaximum
127+
128+instance {-# OVERLAPPABLE #-} HasSchema s Schema
129+ => HasMinimum s (Maybe Scientific) where
130+ minimum_ = schema.minimum_
131+
132+instance {-# OVERLAPPABLE #-} HasSchema s Schema
133+ => HasExclusiveMinimum s (Maybe Bool) where
134+ exclusiveMinimum = schema.exclusiveMinimum
135+
136+instance {-# OVERLAPPABLE #-} HasSchema s Schema
137+ => HasMaxLength s (Maybe Integer) where
138+ maxLength = schema.maxLength
139+
140+instance {-# OVERLAPPABLE #-} HasSchema s Schema
141+ => HasMinLength s (Maybe Integer) where
142+ minLength = schema.minLength
143+
144+instance {-# OVERLAPPABLE #-} HasSchema s Schema
145+ => HasPattern s (Maybe Text) where
146+ pattern = schema.pattern
147+
148+instance {-# OVERLAPPABLE #-} HasSchema s Schema
149+ => HasMaxItems s (Maybe Integer) where
150+ maxItems = schema.maxItems
151+
152+instance {-# OVERLAPPABLE #-} HasSchema s Schema
153+ => HasMinItems s (Maybe Integer) where
154+ minItems = schema.minItems
155+
156+instance {-# OVERLAPPABLE #-} HasSchema s Schema
157+ => HasUniqueItems s (Maybe Bool) where
158+ uniqueItems = schema.uniqueItems
159+
160+instance {-# OVERLAPPABLE #-} HasSchema s Schema
161+ => HasEnum s (Maybe [Value]) where
162+ enum_ = schema.enum_
163+
164+instance {-# OVERLAPPABLE #-} HasSchema s Schema
165+ => HasMultipleOf s (Maybe Scientific) where
166+ multipleOf = schema.multipleOf
src/Data/Swagger/Operation.hs → src/Data/OpenApi/Operation.hsrenamed+140−31
@@ -1,13 +1,13 @@
1{-# LANGUAGE RankNTypes #-}
1{-# LANGUAGE RankNTypes #-}
2-- |
2-- |
3−-- Module: Data.Swagger.Operation
3+-- Module: Data.OpenApi.Operation
4-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
4-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
5-- Stability: experimental
5-- Stability: experimental
6--
6--
7-- Helper traversals and functions for Swagger operations manipulations.
7-- Helper traversals and functions for Swagger operations manipulations.
8-- These might be useful when you already have Swagger specification
8-- These might be useful when you already have Swagger specification
9-- generated by something else.
9-- generated by something else.
10−module Data.Swagger.Operation (
10+module Data.OpenApi.Operation (
11 -- * Operation traversals
11 -- * Operation traversals
12 allOperations,
12 allOperations,
13 operationsOf,
13 operationsOf,
@@ -40,11 +40,12 @@ import Data.Maybe (mapMaybe)
40import Data.Proxy
40import Data.Proxy
41import qualified Data.Set as Set
41import qualified Data.Set as Set
42import Data.Text (Text)
42import Data.Text (Text)
43+import Network.HTTP.Media (MediaType)
43
44
44−import Data.Swagger.Declare
45+import Data.OpenApi.Declare
45−import Data.Swagger.Internal
46+import Data.OpenApi.Internal
46−import Data.Swagger.Lens
47+import Data.OpenApi.Lens
47−import Data.Swagger.Schema
48+import Data.OpenApi.Schema
48
49
49import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
50import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
50import qualified Data.HashSet.InsOrd as InsOrdHS
51import qualified Data.HashSet.InsOrd as InsOrdHS
@@ -53,14 +54,18 @@ import qualified Data.HashSet.InsOrd as InsOrdHS
53-- >>> import Data.Aeson
54-- >>> import Data.Aeson
54-- >>> import Data.Proxy
55-- >>> import Data.Proxy
55-- >>> import Data.Time
56-- >>> import Data.Time
57+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
58+-- >>> import Data.OpenApi.Internal.Utils
56
59
57-- | Prepend path piece to all operations of the spec.
60-- | Prepend path piece to all operations of the spec.
58-- Leading and trailing slashes are trimmed/added automatically.
61-- Leading and trailing slashes are trimmed/added automatically.
59--
62--
60−-- >>> let api = (mempty :: Swagger) & paths .~ [("/info", mempty)]
63+-- >>> let api = (mempty :: OpenApi) & paths .~ [("/info", mempty)]
61−-- >>> encode $ prependPath "user/{user_id}" api ^. paths
64+-- >>> BSL.putStrLn $ encodePretty $ prependPath "user/{user_id}" api ^. paths
65+-- {
62−-- "{\"/user/{user_id}/info\":{}}"
66+-- "/user/{user_id}/info": {}
67+-- }
63−prependPath :: FilePath -> Swagger -> Swagger
68+prependPath :: FilePath -> OpenApi -> OpenApi
64prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
69prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
65 where
70 where
66 x </> y = case trim y of
71 x </> y = case trim y of
@@ -70,7 +75,7 @@ prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
70 trim = dropWhile (== '/') . dropWhileEnd (== '/')
75 trim = dropWhile (== '/') . dropWhileEnd (== '/')
71
76
72-- | All operations of a Swagger spec.
77-- | All operations of a Swagger spec.
73−allOperations :: Traversal' Swagger Operation
78+allOperations :: Traversal' OpenApi Operation
74allOperations = paths.traverse.template
79allOperations = paths.traverse.template
75
80
76-- | @'operationsOf' sub@ will traverse only those operations
81-- | @'operationsOf' sub@ will traverse only those operations
@@ -78,13 +83,66 @@ allOperations = paths.traverse.template
78-- by both path and method.
83-- by both path and method.
79--
84--
80-- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"
85-- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"
81−-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
86+-- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
82−-- >>> let sub = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
87+-- >>> let sub = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]
83−-- >>> encode api
88+-- >>> BSL.putStrLn $ encodePretty api
84−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
89+-- {
90+-- "components": {},
91+-- "info": {
92+-- "title": "",
93+-- "version": ""
94+-- },
95+-- "openapi": "3.0.0",
96+-- "paths": {
97+-- "/user": {
98+-- "get": {
99+-- "responses": {
100+-- "200": {
101+-- "description": "OK"
102+-- }
103+-- }
104+-- },
105+-- "post": {
106+-- "responses": {
107+-- "200": {
108+-- "description": "OK"
109+-- }
110+-- }
111+-- }
112+-- }
113+-- }
114+-- }
85−-- >>> encode $ api & operationsOf sub . at 404 ?~ "Not found"
115+-- >>> BSL.putStrLn $ encodePretty $ api & operationsOf sub . at 404 ?~ "Not found"
86−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"404\":{\"description\":\"Not found\"},\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
116+-- {
117+-- "components": {},
118+-- "info": {
119+-- "title": "",
120+-- "version": ""
121+-- },
122+-- "openapi": "3.0.0",
123+-- "paths": {
124+-- "/user": {
125+-- "get": {
126+-- "responses": {
127+-- "200": {
128+-- "description": "OK"
129+-- },
130+-- "404": {
131+-- "description": "Not found"
132+-- }
133+-- }
134+-- },
135+-- "post": {
136+-- "responses": {
137+-- "200": {
138+-- "description": "OK"
139+-- }
140+-- }
141+-- }
142+-- }
143+-- }
144+-- }
87−operationsOf :: Swagger -> Traversal' Swagger Operation
145+operationsOf :: OpenApi -> Traversal' OpenApi Operation
88operationsOf sub = paths.itraversed.withIndex.subops
146operationsOf sub = paths.itraversed.withIndex.subops
89 where
147 where
90 -- | Traverse operations that correspond to paths and methods of the sub API.
148 -- | Traverse operations that correspond to paths and methods of the sub API.
@@ -106,12 +164,12 @@ operationsOf sub = paths.itraversed.withIndex.subops
106-- @
164-- @
107-- 'applyTags' = 'applyTagsFor' 'allOperations'
165-- 'applyTags' = 'applyTagsFor' 'allOperations'
108-- @
166-- @
109−applyTags :: [Tag] -> Swagger -> Swagger
167+applyTags :: [Tag] -> OpenApi -> OpenApi
110applyTags = applyTagsFor allOperations
168applyTags = applyTagsFor allOperations
111
169
112-- | Apply tags to a part of Swagger spec and update the global
170-- | Apply tags to a part of Swagger spec and update the global
113-- list of tags.
171-- list of tags.
114−applyTagsFor :: Traversal' Swagger Operation -> [Tag] -> Swagger -> Swagger
172+applyTagsFor :: Traversal' OpenApi Operation -> [Tag] -> OpenApi -> OpenApi
115applyTagsFor ops ts swag = swag
173applyTagsFor ops ts swag = swag
116 & ops . tags %~ (<> InsOrdHS.fromList (map _tagName ts))
174 & ops . tags %~ (<> InsOrdHS.fromList (map _tagName ts))
117 & tags %~ (<> InsOrdHS.fromList ts)
175 & tags %~ (<> InsOrdHS.fromList ts)
@@ -121,9 +179,27 @@ applyTagsFor ops ts swag = swag
121--
179--
122-- FIXME doc
180-- FIXME doc
123--
181--
124−-- >>> encode $ runDeclare (declareResponse (Proxy :: Proxy Day)) mempty
182+-- >>> BSL.putStrLn $ encodePretty $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty
125−-- "[{\"Day\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}},{\"description\":\"\",\"schema\":{\"$ref\":\"#/definitions/Day\"}}]"
183+-- [
184+-- {
185+-- "Day": {
186+-- "example": "2016-07-22",
187+-- "format": "date",
188+-- "type": "string"
189+-- }
190+-- },
191+-- {
192+-- "content": {
193+-- "application/json": {
194+-- "schema": {
195+-- "$ref": "#/components/schemas/Day"
196+-- }
197+-- }
198+-- },
199+-- "description": ""
200+-- }
201+-- ]
126−declareResponse :: ToSchema a => Text -> Proxy a -> Declare (Definitions Schema) Response
202+declareResponse :: ToSchema a => MediaType -> Proxy a -> Declare (Definitions Schema) Response
127declareResponse cType proxy = do
203declareResponse cType proxy = do
128 s <- declareSchemaRef proxy
204 s <- declareSchemaRef proxy
129 return (mempty & content.at cType ?~ (mempty & schema ?~ s))
205 return (mempty & content.at cType ?~ (mempty & schema ?~ s))
@@ -139,13 +215,46 @@ declareResponse cType proxy = do
139--
215--
140-- Example:
216-- Example:
141--
217--
142−-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
218+-- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]
143−-- >>> let res = declareResponse (Proxy :: Proxy Day)
219+-- >>> let res = declareResponse "application/json" (Proxy :: Proxy Day)
144−-- >>> encode $ api & setResponse 200 res
220+-- >>> BSL.putStrLn $ encodePretty $ api & setResponse 200 res
145−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"200\":{\"schema\":{\"$ref\":\"#/definitions/Day\"},\"description\":\"\"}}}}},\"definitions\":{\"Day\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}}}"
221+-- {
222+-- "components": {
223+-- "schemas": {
224+-- "Day": {
225+-- "example": "2016-07-22",
226+-- "format": "date",
227+-- "type": "string"
228+-- }
229+-- }
230+-- },
231+-- "info": {
232+-- "title": "",
233+-- "version": ""
234+-- },
235+-- "openapi": "3.0.0",
236+-- "paths": {
237+-- "/user": {
238+-- "get": {
239+-- "responses": {
240+-- "200": {
241+-- "content": {
242+-- "application/json": {
243+-- "schema": {
244+-- "$ref": "#/components/schemas/Day"
245+-- }
246+-- }
247+-- },
248+-- "description": ""
249+-- }
250+-- }
251+-- }
252+-- }
253+-- }
254+-- }
146--
255--
147-- See also @'setResponseWith'@.
256-- See also @'setResponseWith'@.
148−setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
257+setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
149setResponse = setResponseFor allOperations
258setResponse = setResponseFor allOperations
150
259
151-- | Set or update response for all operations.
260-- | Set or update response for all operations.
@@ -159,7 +268,7 @@ setResponse = setResponseFor allOperations
159-- @
268-- @
160--
269--
161-- See also @'setResponse'@.
270-- See also @'setResponse'@.
162−setResponseWith :: (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
271+setResponseWith :: (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
163setResponseWith = setResponseForWith allOperations
272setResponseWith = setResponseForWith allOperations
164
273
165-- | Set response for specified operations.
274-- | Set response for specified operations.
@@ -168,7 +277,7 @@ setResponseWith = setResponseForWith allOperations
168-- If the response already exists it will be overwritten.
277-- If the response already exists it will be overwritten.
169--
278--
170-- See also @'setResponseForWith'@.
279-- See also @'setResponseForWith'@.
171−setResponseFor :: Traversal' Swagger Operation -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
280+setResponseFor :: Traversal' OpenApi Operation -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
172setResponseFor ops code dres swag = swag
281setResponseFor ops code dres swag = swag
173 & components.schemas %~ (<> defs)
282 & components.schemas %~ (<> defs)
174 & ops . at code ?~ Inline res
283 & ops . at code ?~ Inline res
@@ -182,7 +291,7 @@ setResponseFor ops code dres swag = swag
182-- then just the new response is used.
291-- then just the new response is used.
183--
292--
184-- See also @'setResponseFor'@.
293-- See also @'setResponseFor'@.
185−setResponseForWith :: Traversal' Swagger Operation -> (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
294+setResponseForWith :: Traversal' OpenApi Operation -> (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
186setResponseForWith ops f code dres swag = swag
295setResponseForWith ops f code dres swag = swag
187 & components.schemas %~ (<> defs)
296 & components.schemas %~ (<> defs)
188 & ops . at code %~ Just . Inline . combine
297 & ops . at code %~ Just . Inline . combine
src/Data/OpenApi/Optics.hsnew file+328
@@ -0,0 +1,328 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE OverloadedLabels #-}
5+{-# LANGUAGE TemplateHaskell #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE UndecidableInstances #-}
8+{-# OPTIONS_GHC -fno-warn-orphans #-}
9+-- |
10+-- Module: Data.OpenApi.Optics
11+-- Maintainer: Andrzej Rybczak <andrzej@rybczak.net>
12+-- Stability: experimental
13+--
14+-- Lenses and prisms for the <https://hackage.haskell.org/package/optics optics>
15+-- library.
16+--
17+-- >>> import Data.Aeson
18+-- >>> import Optics.Core
19+-- >>> :set -XOverloadedLabels
20+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
21+--
22+-- Example from the "Data.OpenApi" module using @optics@:
23+--
24+-- >>> :{
25+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
26+-- & #components % #schemas .~ [ ("User", mempty & #type ?~ OpenApiString) ]
27+-- & #paths .~
28+-- [ ("/user", mempty & #get ?~ (mempty
29+-- & at 200 ?~ ("OK" & #_Inline % #content % at "application/json" ?~ (mempty & #schema ?~ Ref (Reference "User")))
30+-- & at 404 ?~ "User info not found")) ]
31+-- :}
32+-- {
33+-- "components": {
34+-- "schemas": {
35+-- "User": {
36+-- "type": "string"
37+-- }
38+-- }
39+-- },
40+-- "info": {
41+-- "title": "",
42+-- "version": ""
43+-- },
44+-- "openapi": "3.0.0",
45+-- "paths": {
46+-- "/user": {
47+-- "get": {
48+-- "responses": {
49+-- "200": {
50+-- "content": {
51+-- "application/json": {
52+-- "schema": {
53+-- "$ref": "#/components/schemas/User"
54+-- }
55+-- }
56+-- },
57+-- "description": "OK"
58+-- },
59+-- "404": {
60+-- "description": "User info not found"
61+-- }
62+-- }
63+-- }
64+-- }
65+-- }
66+-- }
67+--
68+-- For convenience optics are defined as /labels/. It means that field accessor
69+-- names can be overloaded for different types. One such common field is
70+-- @#description@. Many components of a Swagger specification can have
71+-- descriptions, and you can use the same name for them:
72+--
73+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & #description .~ "No content"
74+-- {
75+-- "description": "No content"
76+-- }
77+-- >>> :{
78+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
79+-- & #type ?~ OpenApiBoolean
80+-- & #description ?~ "To be or not to be"
81+-- :}
82+-- {
83+-- "description": "To be or not to be",
84+-- "type": "boolean"
85+-- }
86+--
87+-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and
88+-- @'Responses'@ have direct access to it via @'Optics.Core.At.at'@. Example:
89+--
90+-- >>> :{
91+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
92+-- & at 404 ?~ "Not found"
93+-- :}
94+-- {
95+-- "responses": {
96+-- "404": {
97+-- "description": "Not found"
98+-- }
99+-- }
100+-- }
101+module Data.OpenApi.Optics () where
102+
103+import Data.Aeson (Value)
104+import Data.Scientific (Scientific)
105+import Data.OpenApi.Internal
106+import Data.OpenApi.Internal.Utils
107+import Data.Text (Text)
108+import Optics.Core
109+import Optics.TH
110+
111+-- Lenses
112+
113+makeFieldLabels ''OpenApi
114+makeFieldLabels ''Components
115+makeFieldLabels ''Server
116+makeFieldLabels ''ServerVariable
117+makeFieldLabels ''RequestBody
118+makeFieldLabels ''MediaTypeObject
119+makeFieldLabels ''Info
120+makeFieldLabels ''Contact
121+makeFieldLabels ''License
122+makeFieldLabels ''PathItem
123+makeFieldLabels ''Tag
124+makeFieldLabels ''Operation
125+makeFieldLabels ''Param
126+makeFieldLabels ''Header
127+makeFieldLabels ''Schema
128+makeFieldLabels ''NamedSchema
129+makeFieldLabels ''Xml
130+makeFieldLabels ''Responses
131+makeFieldLabels ''Response
132+makeFieldLabels ''SecurityScheme
133+makeFieldLabels ''ApiKeyParams
134+makeFieldLabels ''OAuth2ImplicitFlow
135+makeFieldLabels ''OAuth2PasswordFlow
136+makeFieldLabels ''OAuth2ClientCredentialsFlow
137+makeFieldLabels ''OAuth2AuthorizationCodeFlow
138+makeFieldLabels ''OAuth2Flow
139+makeFieldLabels ''OAuth2Flows
140+makeFieldLabels ''ExternalDocs
141+makeFieldLabels ''Encoding
142+makeFieldLabels ''Example
143+makeFieldLabels ''Discriminator
144+makeFieldLabels ''Link
145+
146+-- Prisms
147+
148+makePrismLabels ''SecuritySchemeType
149+makePrismLabels ''Referenced
150+
151+-- OpenApiItems prisms
152+
153+instance
154+ ( a ~ [Referenced Schema]
155+ , b ~ [Referenced Schema]
156+ ) => LabelOptic "_OpenApiItemsArray"
157+ A_Review
158+ OpenApiItems
159+ OpenApiItems
160+ a
161+ b where
162+ labelOptic = unto (\x -> OpenApiItemsArray x)
163+ {-# INLINE labelOptic #-}
164+
165+instance
166+ ( a ~ Referenced Schema
167+ , b ~ Referenced Schema
168+ ) => LabelOptic "_OpenApiItemsObject"
169+ A_Review
170+ OpenApiItems
171+ OpenApiItems
172+ a
173+ b where
174+ labelOptic = unto (\x -> OpenApiItemsObject x)
175+ {-# INLINE labelOptic #-}
176+
177+-- =============================================================
178+-- More helpful instances for easier access to schema properties
179+
180+type instance Index Responses = HttpStatusCode
181+type instance Index Operation = HttpStatusCode
182+
183+type instance IxValue Responses = Referenced Response
184+type instance IxValue Operation = Referenced Response
185+
186+instance Ixed Responses where
187+ ix n = #responses % ix n
188+ {-# INLINE ix #-}
189+instance At Responses where
190+ at n = #responses % at n
191+ {-# INLINE at #-}
192+
193+instance Ixed Operation where
194+ ix n = #responses % ix n
195+ {-# INLINE ix #-}
196+instance At Operation where
197+ at n = #responses % at n
198+ {-# INLINE at #-}
199+
200+-- #type
201+
202+instance
203+ ( a ~ Maybe OpenApiType
204+ , b ~ Maybe OpenApiType
205+ ) => LabelOptic "type" A_Lens NamedSchema NamedSchema a b where
206+ labelOptic = #schema % #type
207+ {-# INLINE labelOptic #-}
208+
209+-- #default
210+
211+instance
212+ ( a ~ Maybe Value, b ~ Maybe Value
213+ ) => LabelOptic "default" A_Lens NamedSchema NamedSchema a b where
214+ labelOptic = #schema % #default
215+ {-# INLINE labelOptic #-}
216+
217+-- #format
218+
219+instance
220+ ( a ~ Maybe Format, b ~ Maybe Format
221+ ) => LabelOptic "format" A_Lens NamedSchema NamedSchema a b where
222+ labelOptic = #schema % #format
223+ {-# INLINE labelOptic #-}
224+
225+-- #items
226+
227+instance
228+ ( a ~ Maybe OpenApiItems
229+ , b ~ Maybe OpenApiItems
230+ ) => LabelOptic "items" A_Lens NamedSchema NamedSchema a b where
231+ labelOptic = #schema % #items
232+ {-# INLINE labelOptic #-}
233+
234+-- #maximum
235+
236+instance
237+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
238+ ) => LabelOptic "maximum" A_Lens NamedSchema NamedSchema a b where
239+ labelOptic = #schema % #maximum
240+ {-# INLINE labelOptic #-}
241+
242+-- #exclusiveMaximum
243+
244+instance
245+ ( a ~ Maybe Bool, b ~ Maybe Bool
246+ ) => LabelOptic "exclusiveMaximum" A_Lens NamedSchema NamedSchema a b where
247+ labelOptic = #schema % #exclusiveMaximum
248+ {-# INLINE labelOptic #-}
249+
250+-- #minimum
251+
252+instance
253+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
254+ ) => LabelOptic "minimum" A_Lens NamedSchema NamedSchema a b where
255+ labelOptic = #schema % #minimum
256+ {-# INLINE labelOptic #-}
257+
258+-- #exclusiveMinimum
259+
260+instance
261+ ( a ~ Maybe Bool, b ~ Maybe Bool
262+ ) => LabelOptic "exclusiveMinimum" A_Lens NamedSchema NamedSchema a b where
263+ labelOptic = #schema % #exclusiveMinimum
264+ {-# INLINE labelOptic #-}
265+
266+-- #maxLength
267+
268+instance
269+ ( a ~ Maybe Integer, b ~ Maybe Integer
270+ ) => LabelOptic "maxLength" A_Lens NamedSchema NamedSchema a b where
271+ labelOptic = #schema % #maxLength
272+ {-# INLINE labelOptic #-}
273+
274+-- #minLength
275+
276+instance
277+ ( a ~ Maybe Integer, b ~ Maybe Integer
278+ ) => LabelOptic "minLength" A_Lens NamedSchema NamedSchema a b where
279+ labelOptic = #schema % #minLength
280+ {-# INLINE labelOptic #-}
281+
282+-- #pattern
283+
284+instance
285+ ( a ~ Maybe Text, b ~ Maybe Text
286+ ) => LabelOptic "pattern" A_Lens NamedSchema NamedSchema a b where
287+ labelOptic = #schema % #pattern
288+ {-# INLINE labelOptic #-}
289+
290+-- #maxItems
291+
292+instance
293+ ( a ~ Maybe Integer, b ~ Maybe Integer
294+ ) => LabelOptic "maxItems" A_Lens NamedSchema NamedSchema a b where
295+ labelOptic = #schema % #maxItems
296+ {-# INLINE labelOptic #-}
297+
298+-- #minItems
299+
300+instance
301+ ( a ~ Maybe Integer, b ~ Maybe Integer
302+ ) => LabelOptic "minItems" A_Lens NamedSchema NamedSchema a b where
303+ labelOptic = #schema % #minItems
304+ {-# INLINE labelOptic #-}
305+
306+-- #uniqueItems
307+
308+instance
309+ ( a ~ Maybe Bool, b ~ Maybe Bool
310+ ) => LabelOptic "uniqueItems" A_Lens NamedSchema NamedSchema a b where
311+ labelOptic = #schema % #uniqueItems
312+ {-# INLINE labelOptic #-}
313+
314+-- #enum
315+
316+instance
317+ ( a ~ Maybe [Value], b ~ Maybe [Value]
318+ ) => LabelOptic "enum" A_Lens NamedSchema NamedSchema a b where
319+ labelOptic = #schema % #enum
320+ {-# INLINE labelOptic #-}
321+
322+-- #multipleOf
323+
324+instance
325+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
326+ ) => LabelOptic "multipleOf" A_Lens NamedSchema NamedSchema a b where
327+ labelOptic = #schema % #multipleOf
328+ {-# INLINE labelOptic #-}
src/Data/Swagger/ParamSchema.hs → src/Data/OpenApi/ParamSchema.hsrenamed+7−7
@@ -1,10 +1,10 @@
1-- |
1-- |
2−-- Module: Data.Swagger.ParamSchema
2+-- Module: Data.OpenApi.ParamSchema
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
4-- Stability: experimental
4-- Stability: experimental
5--
5--
6-- Types and functions for working with Swagger parameter schema.
6-- Types and functions for working with Swagger parameter schema.
7−module Data.Swagger.ParamSchema (
7+module Data.OpenApi.ParamSchema (
8 -- * Encoding
8 -- * Encoding
9 ToParamSchema(..),
9 ToParamSchema(..),
10
10
@@ -13,14 +13,14 @@ module Data.Swagger.ParamSchema (
13 toParamSchemaBoundedIntegral,
13 toParamSchemaBoundedIntegral,
14
14
15 -- * Schema templates
15 -- * Schema templates
16− passwordParamSchema,
16+ passwordSchema,
17− binaryParamSchema,
17+ binarySchema,
18− byteParamSchema,
18+ byteSchema,
19
19
20 -- * Generic encoding configuration
20 -- * Generic encoding configuration
21 SchemaOptions(..),
21 SchemaOptions(..),
22 defaultSchemaOptions,
22 defaultSchemaOptions,
23) where
23) where
24
24
25−import Data.Swagger.Internal.ParamSchema
25+import Data.OpenApi.Internal.ParamSchema
26−import Data.Swagger.SchemaOptions
26+import Data.OpenApi.SchemaOptions
src/Data/Swagger/Schema.hs → src/Data/OpenApi/Schema.hsrenamed+4−13
@@ -1,10 +1,10 @@
1-- |
1-- |
2−-- Module: Data.Swagger.Schema
2+-- Module: Data.OpenApi.Schema
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
4-- Stability: experimental
4-- Stability: experimental
5--
5--
6-- Types and functions for working with Swagger schema.
6-- Types and functions for working with Swagger schema.
7−module Data.Swagger.Schema (
7+module Data.OpenApi.Schema (
8 -- * Encoding
8 -- * Encoding
9 ToSchema(..),
9 ToSchema(..),
10 declareSchema,
10 declareSchema,
@@ -32,15 +32,6 @@ module Data.Swagger.Schema (
32 paramSchemaToNamedSchema,
32 paramSchemaToNamedSchema,
33 paramSchemaToSchema,
33 paramSchemaToSchema,
34
34
35− -- ** Unrestricted versions
36− genericDeclareNamedSchemaUnrestricted,
37− genericDeclareSchemaUnrestricted,
38−
39− -- * Schema templates
40− passwordSchema,
41− binarySchema,
42− byteSchema,
43−
44 -- * Sketching @'Schema'@s using @'ToJSON'@
35 -- * Sketching @'Schema'@s using @'ToJSON'@
45 sketchSchema,
36 sketchSchema,
46 sketchStrictSchema,
37 sketchStrictSchema,
@@ -57,5 +48,5 @@ module Data.Swagger.Schema (
57 fromAesonOptions,
48 fromAesonOptions,
58) where
49) where
59
50
60−import Data.Swagger.Internal.Schema
51+import Data.OpenApi.Internal.Schema
61−import Data.Swagger.SchemaOptions
52+import Data.OpenApi.SchemaOptions
src/Data/Swagger/Schema/Generator.hs → src/Data/OpenApi/Schema/Generator.hsrenamed+23−19
@@ -2,7 +2,7 @@
2{-# LANGUAGE OverloadedLists #-}
2{-# LANGUAGE OverloadedLists #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4
4
5−module Data.Swagger.Schema.Generator where
5+module Data.OpenApi.Schema.Generator where
6
6
7import Prelude ()
7import Prelude ()
8import Prelude.Compat
8import Prelude.Compat
@@ -16,20 +16,24 @@ import Data.Maybe
16import Data.Proxy
16import Data.Proxy
17import Data.Scientific
17import Data.Scientific
18import qualified Data.Set as S
18import qualified Data.Set as S
19−import Data.Swagger
19+import Data.OpenApi
20−import Data.Swagger.Declare
20+import Data.OpenApi.Declare
21−import Data.Swagger.Internal.Schema.Validation (inferSchemaTypes)
21+import Data.OpenApi.Internal.Schema.Validation (inferSchemaTypes)
22import qualified Data.Text as T
22import qualified Data.Text as T
23import qualified Data.Vector as V
23import qualified Data.Vector as V
24import Test.QuickCheck (arbitrary)
24import Test.QuickCheck (arbitrary)
25import Test.QuickCheck.Gen
25import Test.QuickCheck.Gen
26import Test.QuickCheck.Property
26import Test.QuickCheck.Property
27
27
28+import Data.OpenApi.Aeson.Compat (fromInsOrdHashMap)
29+
28-- | Note: 'schemaGen' may 'error', if schema type is not specified,
30-- | Note: 'schemaGen' may 'error', if schema type is not specified,
29-- and cannot be inferred.
31-- and cannot be inferred.
30schemaGen :: Definitions Schema -> Schema -> Gen Value
32schemaGen :: Definitions Schema -> Schema -> Gen Value
31schemaGen _ schema
33schemaGen _ schema
32− | Just cases <- schema ^. paramSchema . enum_ = elements cases
34+ | Just cases <- schema ^. enum_ = elements cases
35+schemaGen defns schema
36+ | Just variants <- schema ^. oneOf = schemaGen defns =<< elements (dereference defns <$> variants)
33schemaGen defns schema =
37schemaGen defns schema =
34 case schema ^. type_ of
38 case schema ^. type_ of
35 Nothing ->
39 Nothing ->
@@ -37,25 +41,25 @@ schemaGen defns schema =
37 [ inferredType ] -> schemaGen defns (schema & type_ ?~ inferredType)
41 [ inferredType ] -> schemaGen defns (schema & type_ ?~ inferredType)
38 -- Gen is not MonadFail
42 -- Gen is not MonadFail
39 _ -> error "unable to infer schema type"
43 _ -> error "unable to infer schema type"
40− Just SwaggerBoolean -> Bool <$> elements [True, False]
44+ Just OpenApiBoolean -> Bool <$> elements [True, False]
41− Just SwaggerNull -> pure Null
45+ Just OpenApiNull -> pure Null
42− Just SwaggerNumber
46+ Just OpenApiNumber
43 | Just min <- schema ^. minimum_
47 | Just min <- schema ^. minimum_
44 , Just max <- schema ^. maximum_ ->
48 , Just max <- schema ^. maximum_ ->
45 Number . fromFloatDigits <$>
49 Number . fromFloatDigits <$>
46 choose (toRealFloat min, toRealFloat max :: Double)
50 choose (toRealFloat min, toRealFloat max :: Double)
47 | otherwise -> Number .fromFloatDigits <$> (arbitrary :: Gen Double)
51 | otherwise -> Number .fromFloatDigits <$> (arbitrary :: Gen Double)
48− Just SwaggerInteger
52+ Just OpenApiInteger
49 | Just min <- schema ^. minimum_
53 | Just min <- schema ^. minimum_
50 , Just max <- schema ^. maximum_ ->
54 , Just max <- schema ^. maximum_ ->
51 Number . fromInteger <$>
55 Number . fromInteger <$>
52 choose (truncate min, truncate max)
56 choose (truncate min, truncate max)
53 | otherwise -> Number . fromInteger <$> arbitrary
57 | otherwise -> Number . fromInteger <$> arbitrary
54− Just SwaggerArray
58+ Just OpenApiArray
55 | Just 0 <- schema ^. maxLength -> pure $ Array V.empty
59 | Just 0 <- schema ^. maxLength -> pure $ Array V.empty
56 | Just items <- schema ^. items ->
60 | Just items <- schema ^. items ->
57 case items of
61 case items of
58− SwaggerItemsObject ref -> do
62+ OpenApiItemsObject ref -> do
59 size <- getSize
63 size <- getSize
60 let itemSchema = dereference defns ref
64 let itemSchema = dereference defns ref
61 minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minItems
65 minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minItems
@@ -63,17 +67,17 @@ schemaGen defns schema =
63 arrayLength <- choose (minLength', max minLength' maxLength')
67 arrayLength <- choose (minLength', max minLength' maxLength')
64 generatedArray <- vectorOf arrayLength $ schemaGen defns itemSchema
68 generatedArray <- vectorOf arrayLength $ schemaGen defns itemSchema
65 return . Array $ V.fromList generatedArray
69 return . Array $ V.fromList generatedArray
66− SwaggerItemsArray refs ->
70+ OpenApiItemsArray refs ->
67 let itemGens = schemaGen defns . dereference defns <$> refs
71 let itemGens = schemaGen defns . dereference defns <$> refs
68 in fmap (Array . V.fromList) $ sequence itemGens
72 in fmap (Array . V.fromList) $ sequence itemGens
69− Just SwaggerString -> do
73+ Just OpenApiString -> do
70 size <- getSize
74 size <- getSize
71 let minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minLength
75 let minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minLength
72 let maxLength' = fromMaybe size $ fromInteger <$> schema ^. maxLength
76 let maxLength' = fromMaybe size $ fromInteger <$> schema ^. maxLength
73 length <- choose (minLength', max minLength' maxLength')
77 length <- choose (minLength', max minLength' maxLength')
74 str <- vectorOf length arbitrary
78 str <- vectorOf length arbitrary
75 return . String $ T.pack str
79 return . String $ T.pack str
76− Just SwaggerObject -> do
80+ Just OpenApiObject -> do
77 size <- getSize
81 size <- getSize
78 let props = dereference defns <$> schema ^. properties
82 let props = dereference defns <$> schema ^. properties
79 reqKeys = S.fromList $ schema ^. required
83 reqKeys = S.fromList $ schema ^. required
@@ -93,11 +97,11 @@ schemaGen defns schema =
93 return . M.fromList $ zip additionalKeys (repeat . schemaGen defns $ dereference defns addlSchema)
97 return . M.fromList $ zip additionalKeys (repeat . schemaGen defns $ dereference defns addlSchema)
94 _ -> return []
98 _ -> return []
95 x <- sequence $ gens <> additionalGens
99 x <- sequence $ gens <> additionalGens
96− return . Object $ M.toHashMap x
100+ return . Object $ fromInsOrdHashMap x
97− where
101+
98− dereference :: Definitions a -> Referenced a -> a
102+dereference :: Definitions a -> Referenced a -> a
99− dereference _ (Inline a) = a
103+dereference _ (Inline a) = a
100− dereference defs (Ref (Reference ref)) = fromJust $ M.lookup ref defs
104+dereference defs (Ref (Reference ref)) = fromJust $ M.lookup ref defs
101
105
102genValue :: (ToSchema a) => Proxy a -> Gen Value
106genValue :: (ToSchema a) => Proxy a -> Gen Value
103genValue p =
107genValue p =
src/Data/Swagger/Schema/Validation.hs → src/Data/OpenApi/Schema/Validation.hsrenamed+7−7
@@ -1,10 +1,10 @@
1-- |
1-- |
2−-- Module: Data.Swagger.Schema.Validation
2+-- Module: Data.OpenApi.Schema.Validation
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
3-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
4-- Stability: experimental
4-- Stability: experimental
5--
5--
6-- Validate JSON values with Swagger Schema.
6-- Validate JSON values with Swagger Schema.
7−module Data.Swagger.Schema.Validation (
7+module Data.OpenApi.Schema.Validation (
8 -- * How to use validation
8 -- * How to use validation
9 -- $howto
9 -- $howto
10
10
@@ -29,13 +29,13 @@ module Data.Swagger.Schema.Validation (
29 validateJSONWithPatternChecker,
29 validateJSONWithPatternChecker,
30) where
30) where
31
31
32−import Data.Swagger.Internal.Schema.Validation
32+import Data.OpenApi.Internal.Schema.Validation
33
33
34-- $setup
34-- $setup
35-- >>> import Control.Lens
35-- >>> import Control.Lens
36-- >>> import Data.Aeson
36-- >>> import Data.Aeson
37-- >>> import Data.Proxy
37-- >>> import Data.Proxy
38−-- >>> import Data.Swagger
38+-- >>> import Data.OpenApi
39-- >>> import GHC.Generics
39-- >>> import GHC.Generics
40-- >>> :set -XDeriveGeneric
40-- >>> :set -XDeriveGeneric
41
41
@@ -71,11 +71,11 @@ import Data.Swagger.Internal.Schema.Validation
71-- generally fails for @null@ JSON:
71-- generally fails for @null@ JSON:
72--
72--
73-- >>> validateToJSON (Nothing :: Maybe String)
73-- >>> validateToJSON (Nothing :: Maybe String)
74−-- ["expected JSON value of type SwaggerString"]
74+-- ["expected JSON value of type OpenApiString"]
75-- >>> validateToJSON ([Just "hello", Nothing] :: [Maybe String])
75-- >>> validateToJSON ([Just "hello", Nothing] :: [Maybe String])
76−-- ["expected JSON value of type SwaggerString"]
76+-- ["expected JSON value of type OpenApiString"]
77-- >>> validateToJSON (123, Nothing :: Maybe String)
77-- >>> validateToJSON (123, Nothing :: Maybe String)
78−-- ["expected JSON value of type SwaggerString"]
78+-- ["expected JSON value of type OpenApiString"]
79--
79--
80-- However, when @'Maybe' a@ is a type of a record field,
80-- However, when @'Maybe' a@ is a type of a record field,
81-- validation takes @'required'@ property of the @'Schema'@
81-- validation takes @'required'@ property of the @'Schema'@
src/Data/Swagger/SchemaOptions.hs → src/Data/OpenApi/SchemaOptions.hsrenamed+7−3
@@ -1,11 +1,11 @@
1{-# LANGUAGE RecordWildCards #-}
1{-# LANGUAGE RecordWildCards #-}
2-- |
2-- |
3−-- Module: Data.Swagger.SchemaOptions
3+-- Module: Data.OpenApi.SchemaOptions
4-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
4-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
5-- Stability: experimental
5-- Stability: experimental
6--
6--
7-- Generic deriving options for @'ToParamSchema'@ and @'ToSchema'@.
7-- Generic deriving options for @'ToParamSchema'@ and @'ToSchema'@.
8−module Data.Swagger.SchemaOptions where
8+module Data.OpenApi.SchemaOptions where
9
9
10import qualified Data.Aeson.Types as Aeson
10import qualified Data.Aeson.Types as Aeson
11
11
@@ -22,6 +22,8 @@ data SchemaOptions = SchemaOptions
22 , allNullaryToStringTag :: Bool
22 , allNullaryToStringTag :: Bool
23 -- | Hide the field name when a record constructor has only one field, like a newtype.
23 -- | Hide the field name when a record constructor has only one field, like a newtype.
24 , unwrapUnaryRecords :: Bool
24 , unwrapUnaryRecords :: Bool
25+ -- | Specifies how to encode constructors of a sum datatype.
26+ , sumEncoding :: Aeson.SumEncoding
25 }
27 }
26
28
27-- | Default encoding @'SchemaOptions'@.
29-- | Default encoding @'SchemaOptions'@.
@@ -33,6 +35,7 @@ data SchemaOptions = SchemaOptions
33-- , 'datatypeNameModifier' = id
35-- , 'datatypeNameModifier' = id
34-- , 'allNullaryToStringTag' = True
36-- , 'allNullaryToStringTag' = True
35-- , 'unwrapUnaryRecords' = False
37-- , 'unwrapUnaryRecords' = False
38+-- , 'sumEncoding' = 'Aeson.defaultTaggedObject'
36-- }
39-- }
37-- @
40-- @
38defaultSchemaOptions :: SchemaOptions
41defaultSchemaOptions :: SchemaOptions
@@ -42,6 +45,7 @@ defaultSchemaOptions = SchemaOptions
42 , datatypeNameModifier = id
45 , datatypeNameModifier = id
43 , allNullaryToStringTag = True
46 , allNullaryToStringTag = True
44 , unwrapUnaryRecords = False
47 , unwrapUnaryRecords = False
48+ , sumEncoding = Aeson.defaultTaggedObject
45 }
49 }
46
50
47-- | Convert 'Aeson.Options' to 'SchemaOptions'.
51-- | Convert 'Aeson.Options' to 'SchemaOptions'.
@@ -56,7 +60,6 @@ defaultSchemaOptions = SchemaOptions
56-- Note that these fields have no effect on `SchemaOptions`:
60-- Note that these fields have no effect on `SchemaOptions`:
57--
61--
58-- * 'Aeson.omitNothingFields'
62-- * 'Aeson.omitNothingFields'
59−-- * 'Aeson.sumEncoding'
60-- * 'Aeson.tagSingleConstructors'
63-- * 'Aeson.tagSingleConstructors'
61--
64--
62-- The rest is defined as in 'defaultSchemaOptions'.
65-- The rest is defined as in 'defaultSchemaOptions'.
@@ -69,4 +72,5 @@ fromAesonOptions opts = defaultSchemaOptions
69 , constructorTagModifier = Aeson.constructorTagModifier opts
72 , constructorTagModifier = Aeson.constructorTagModifier opts
70 , allNullaryToStringTag = Aeson.allNullaryToStringTag opts
73 , allNullaryToStringTag = Aeson.allNullaryToStringTag opts
71 , unwrapUnaryRecords = Aeson.unwrapUnaryRecords opts
74 , unwrapUnaryRecords = Aeson.unwrapUnaryRecords opts
75+ , sumEncoding = Aeson.sumEncoding opts
72 }
76 }
src/Data/Swagger/Lens.hsdeleted−180
@@ -1,180 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE TemplateHaskell #-}
8−{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE DataKinds #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−{-# OPTIONS_GHC -fno-warn-orphans #-}
12−-- |
13−-- Module: Data.Swagger.Lens
14−-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
15−-- Stability: experimental
16−--
17−-- Lenses and prisms for Swagger.
18−module Data.Swagger.Lens where
19−
20−import Control.Lens
21−import Data.Aeson (Value)
22−import Data.Scientific (Scientific)
23−import Data.Swagger.Internal
24−import Data.Swagger.Internal.Utils
25−import Data.Text (Text)
26−
27−-- * Classy lenses
28−
29−makeFields ''Swagger
30−makeFields ''Components
31−makeFields ''Server
32−-- conflict with enum of ParamSchema
33−--makeLensesWith swaggerFieldRules ''ServerVariable
34−makeFields ''RequestBody
35−makeFields ''MediaTypeObject
36−makeFields ''Info
37−makeFields ''Contact
38−makeFields ''License
39−makeLensesWith swaggerFieldRules ''PathItem
40−makeFields ''Tag
41−makeFields ''Operation
42−makeLensesWith swaggerFieldRules ''Param
43−makeFields ''Header
44−makeLensesWith swaggerFieldRules ''Schema
45−makeFields ''NamedSchema
46−makeLensesWith swaggerFieldRules ''ParamSchema
47−makeFields ''Xml
48−makeLensesWith swaggerFieldRules ''Responses
49−makeFields ''Response
50−makeLensesWith swaggerFieldRules ''SecurityScheme
51−makeFields ''ApiKeyParams
52−makeFields ''OAuth2ImplicitFlow
53−makeFields ''OAuth2PasswordFlow
54−makeFields ''OAuth2ClientCredentialsFlow
55−makeFields ''OAuth2AuthorizationCodeFlow
56−makeFields ''OAuth2Flow
57−makeFields ''OAuth2Flows
58−makeFields ''ExternalDocs
59−makeFields ''Encoding
60−makeFields ''Example
61−makeFields ''Discriminator
62−
63−-- * Prisms
64−-- ** 'SecuritySchemeType' prisms
65−makePrisms ''SecuritySchemeType
66−-- ** 'Referenced' prisms
67−makePrisms ''Referenced
68−
69−-- ** 'SwaggerItems' prisms
70−
71−_SwaggerItemsArray :: Review (SwaggerItems 'SwaggerKindSchema) [Referenced Schema]
72−_SwaggerItemsArray
73− = unto (\x -> SwaggerItemsArray x)
74−{- \x -> case x of
75− SwaggerItemsPrimitive c p -> Left (SwaggerItemsPrimitive c p)
76− SwaggerItemsObject o -> Left (SwaggerItemsObject o)
77− SwaggerItemsArray a -> Right a
78−-}
79−
80−_SwaggerItemsObject :: Review (SwaggerItems 'SwaggerKindSchema) (Referenced Schema)
81−_SwaggerItemsObject
82− = unto (\x -> SwaggerItemsObject x)
83−{- \x -> case x of
84− SwaggerItemsPrimitive c p -> Left (SwaggerItemsPrimitive c p)
85− SwaggerItemsObject o -> Right o
86− SwaggerItemsArray a -> Left (SwaggerItemsArray a)
87−-}
88−
89−_SwaggerItemsPrimitive :: forall t p f. (Profunctor p, Bifunctor p, Functor f) => Optic' p f (SwaggerItems t) (Maybe (CollectionFormat t), ParamSchema t)
90−_SwaggerItemsPrimitive = unto (\(c, p) -> SwaggerItemsPrimitive c p)
91−
92−-- =============================================================
93−-- More helpful instances for easier access to schema properties
94−
95−type instance Index Responses = HttpStatusCode
96−type instance Index Operation = HttpStatusCode
97−
98−type instance IxValue Responses = Referenced Response
99−type instance IxValue Operation = Referenced Response
100−
101−instance Ixed Responses where ix n = responses . ix n
102−instance At Responses where at n = responses . at n
103−
104−instance Ixed Operation where ix n = responses . ix n
105−instance At Operation where at n = responses . at n
106−
107−instance HasParamSchema NamedSchema (ParamSchema 'SwaggerKindSchema) where paramSchema = schema.paramSchema
108−
109−-- HasType instances
110−instance HasType Header (Maybe (SwaggerType ('SwaggerKindNormal Header))) where type_ = paramSchema.type_
111−instance HasType Schema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
112−instance HasType NamedSchema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
113−
114−-- HasDefault instances
115−instance HasDefault Header (Maybe Value) where default_ = paramSchema.default_
116−instance HasDefault Schema (Maybe Value) where default_ = paramSchema.default_
117−
118−-- OVERLAPPABLE instances
119−
120−instance
121− {-# OVERLAPPABLE #-}
122− HasParamSchema s (ParamSchema t)
123− => HasFormat s (Maybe Format) where
124− format = paramSchema.format
125−
126−instance
127− {-# OVERLAPPABLE #-}
128− HasParamSchema s (ParamSchema t)
129− => HasItems s (Maybe (SwaggerItems t)) where
130− items = paramSchema.items
131−
132−instance
133− {-# OVERLAPPABLE #-}
134− HasParamSchema s (ParamSchema t)
135− => HasMaximum s (Maybe Scientific) where
136− maximum_ = paramSchema.maximum_
137−
138−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
139− => HasExclusiveMaximum s (Maybe Bool) where
140− exclusiveMaximum = paramSchema.exclusiveMaximum
141−
142−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
143− => HasMinimum s (Maybe Scientific) where
144− minimum_ = paramSchema.minimum_
145−
146−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
147− => HasExclusiveMinimum s (Maybe Bool) where
148− exclusiveMinimum = paramSchema.exclusiveMinimum
149−
150−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
151− => HasMaxLength s (Maybe Integer) where
152− maxLength = paramSchema.maxLength
153−
154−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
155− => HasMinLength s (Maybe Integer) where
156− minLength = paramSchema.minLength
157−
158−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
159− => HasPattern s (Maybe Text) where
160− pattern = paramSchema.pattern
161−
162−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
163− => HasMaxItems s (Maybe Integer) where
164− maxItems = paramSchema.maxItems
165−
166−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
167− => HasMinItems s (Maybe Integer) where
168− minItems = paramSchema.minItems
169−
170−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
171− => HasUniqueItems s (Maybe Bool) where
172− uniqueItems = paramSchema.uniqueItems
173−
174−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
175− => HasEnum s (Maybe [Value]) where
176− enum_ = paramSchema.enum_
177−
178−instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
179− => HasMultipleOf s (Maybe Scientific) where
180− multipleOf = paramSchema.multipleOf
src/Data/Swagger/Optics.hsdeleted−509
@@ -1,509 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
4−{-# LANGUAGE OverloadedLabels #-}
5−{-# LANGUAGE TemplateHaskell #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE UndecidableInstances #-}
8−{-# OPTIONS_GHC -fno-warn-orphans #-}
9−-- |
10−-- Module: Data.Swagger.Optics
11−-- Maintainer: Andrzej Rybczak <andrzej@rybczak.net>
12−-- Stability: experimental
13−--
14−-- Lenses and prisms for the <https://hackage.haskell.org/package/optics optics>
15−-- library.
16−--
17−-- >>> import Data.Aeson
18−-- >>> import Optics.Core
19−-- >>> :set -XOverloadedLabels
20−--
21−-- Example from the "Data.Swagger" module using @optics@:
22−--
23−-- >>> :{
24−-- encode $ (mempty :: Swagger)
25−-- & #definitions .~ [ ("User", mempty & #type ?~ SwaggerString) ]
26−-- & #paths .~
27−-- [ ("/user", mempty & #get ?~ (mempty
28−-- & #produces ?~ MimeList ["application/json"]
29−-- & at 200 ?~ ("OK" & #_Inline % #schema ?~ Ref (Reference "User"))
30−-- & at 404 ?~ "User info not found")) ]
31−-- :}
32−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"produces\":[\"application/json\"],\"responses\":{\"404\":{\"description\":\"User info not found\"},\"200\":{\"schema\":{\"$ref\":\"#/definitions/User\"},\"description\":\"OK\"}}}}},\"definitions\":{\"User\":{\"type\":\"string\"}}}"
33−--
34−-- For convenience optics are defined as /labels/. It means that field accessor
35−-- names can be overloaded for different types. One such common field is
36−-- @#description@. Many components of a Swagger specification can have
37−-- descriptions, and you can use the same name for them:
38−--
39−-- >>> encode $ (mempty :: Response) & #description .~ "No content"
40−-- "{\"description\":\"No content\"}"
41−-- >>> :{
42−-- encode $ (mempty :: Schema)
43−-- & #type ?~ SwaggerBoolean
44−-- & #description ?~ "To be or not to be"
45−-- :}
46−-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
47−--
48−-- @'ParamSchema'@ is basically the /base schema specification/ and many types
49−-- contain it. So for convenience, all @'ParamSchema'@ fields are transitively
50−-- made fields of the type that has it. For example, you can use @#type@ to
51−-- access @'SwaggerType'@ of @'Header'@ schema without having to use
52−-- @#paramSchema@:
53−--
54−-- >>> encode $ (mempty :: Header) & #type ?~ SwaggerNumber
55−-- "{\"type\":\"number\"}"
56−--
57−-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and
58−-- @'Responses'@ have direct access to it via @'Optics.Core.At.at'@. Example:
59−--
60−-- >>> :{
61−-- encode $ (mempty :: Operation)
62−-- & at 404 ?~ "Not found"
63−-- :}
64−-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
65−--
66−module Data.Swagger.Optics () where
67−
68−import Data.Aeson (Value)
69−import Data.Scientific (Scientific)
70−import Data.Swagger.Internal
71−import Data.Text (Text)
72−import Optics.Core
73−import Optics.TH
74−
75−-- Lenses
76−
77−makeFieldLabels ''Swagger
78−makeFieldLabels ''Components
79−makeFieldLabels ''Server
80−makeFieldLabels ''ServerVariable
81−makeFieldLabels ''RequestBody
82−makeFieldLabels ''MediaTypeObject
83−makeFieldLabels ''Info
84−makeFieldLabels ''Contact
85−makeFieldLabels ''License
86−makeFieldLabels ''PathItem
87−makeFieldLabels ''Tag
88−makeFieldLabels ''Operation
89−makeFieldLabels ''Param
90−makeFieldLabels ''Header
91−makeFieldLabels ''Schema
92−makeFieldLabels ''NamedSchema
93−makeFieldLabels ''ParamSchema
94−makeFieldLabels ''Xml
95−makeFieldLabels ''Responses
96−makeFieldLabels ''Response
97−makeFieldLabels ''SecurityScheme
98−makeFieldLabels ''ApiKeyParams
99−makeFieldLabels ''OAuth2ImplicitFlow
100−makeFieldLabels ''OAuth2PasswordFlow
101−makeFieldLabels ''OAuth2ClientCredentialsFlow
102−makeFieldLabels ''OAuth2AuthorizationCodeFlow
103−makeFieldLabels ''OAuth2Flow
104−makeFieldLabels ''OAuth2Flows
105−makeFieldLabels ''ExternalDocs
106−makeFieldLabels ''Encoding
107−makeFieldLabels ''Example
108−makeFieldLabels ''Discriminator
109−
110−-- Prisms
111−
112−makePrismLabels ''SecuritySchemeType
113−makePrismLabels ''Referenced
114−
115−-- SwaggerItems prisms
116−
117−instance
118− ( a ~ [Referenced Schema]
119− , b ~ [Referenced Schema]
120− ) => LabelOptic "_SwaggerItemsArray"
121− A_Review
122− (SwaggerItems 'SwaggerKindSchema)
123− (SwaggerItems 'SwaggerKindSchema)
124− a
125− b where
126− labelOptic = unto (\x -> SwaggerItemsArray x)
127− {-# INLINE labelOptic #-}
128−
129−instance
130− ( a ~ Referenced Schema
131− , b ~ Referenced Schema
132− ) => LabelOptic "_SwaggerItemsObject"
133− A_Review
134− (SwaggerItems 'SwaggerKindSchema)
135− (SwaggerItems 'SwaggerKindSchema)
136− a
137− b where
138− labelOptic = unto (\x -> SwaggerItemsObject x)
139− {-# INLINE labelOptic #-}
140−
141−instance
142− ( a ~ (Maybe (CollectionFormat t), ParamSchema t)
143− , b ~ (Maybe (CollectionFormat t), ParamSchema t)
144− ) => LabelOptic "_SwaggerItemsPrimitive"
145− A_Review
146− (SwaggerItems t)
147− (SwaggerItems t)
148− a
149− b where
150− labelOptic = unto (\(c, p) -> SwaggerItemsPrimitive c p)
151− {-# INLINE labelOptic #-}
152−
153−-- =============================================================
154−-- More helpful instances for easier access to schema properties
155−
156−type instance Index Responses = HttpStatusCode
157−type instance Index Operation = HttpStatusCode
158−
159−type instance IxValue Responses = Referenced Response
160−type instance IxValue Operation = Referenced Response
161−
162−instance Ixed Responses where
163− ix n = #responses % ix n
164− {-# INLINE ix #-}
165−instance At Responses where
166− at n = #responses % at n
167− {-# INLINE at #-}
168−
169−instance Ixed Operation where
170− ix n = #responses % ix n
171− {-# INLINE ix #-}
172−instance At Operation where
173− at n = #responses % at n
174− {-# INLINE at #-}
175−
176−-- #paramSchema
177−
178−instance
179− ( a ~ ParamSchema 'SwaggerKindSchema
180− , b ~ ParamSchema 'SwaggerKindSchema
181− ) => LabelOptic "paramSchema" A_Lens NamedSchema NamedSchema a b where
182− labelOptic = #schema % #paramSchema
183− {-# INLINE labelOptic #-}
184−
185−-- #type
186−
187−instance
188− ( a ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
189− , b ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
190− ) => LabelOptic "type" A_Lens Header Header a b where
191− labelOptic = #paramSchema % #type
192− {-# INLINE labelOptic #-}
193−
194−instance
195− ( a ~ Maybe (SwaggerType 'SwaggerKindSchema)
196− , b ~ Maybe (SwaggerType 'SwaggerKindSchema)
197− ) => LabelOptic "type" A_Lens Schema Schema a b where
198− labelOptic = #paramSchema % #type
199− {-# INLINE labelOptic #-}
200−
201−instance
202− ( a ~ Maybe (SwaggerType 'SwaggerKindSchema)
203− , b ~ Maybe (SwaggerType 'SwaggerKindSchema)
204− ) => LabelOptic "type" A_Lens NamedSchema NamedSchema a b where
205− labelOptic = #paramSchema % #type
206− {-# INLINE labelOptic #-}
207−
208−-- #default
209−
210−instance
211− ( a ~ Maybe Value, b ~ Maybe Value
212− ) => LabelOptic "default" A_Lens Header Header a b where
213− labelOptic = #paramSchema % #default
214− {-# INLINE labelOptic #-}
215−
216−instance
217− ( a ~ Maybe Value, b ~ Maybe Value
218− ) => LabelOptic "default" A_Lens Schema Schema a b where
219− labelOptic = #paramSchema % #default
220− {-# INLINE labelOptic #-}
221−
222−instance
223− ( a ~ Maybe Value, b ~ Maybe Value
224− ) => LabelOptic "default" A_Lens NamedSchema NamedSchema a b where
225− labelOptic = #paramSchema % #default
226− {-# INLINE labelOptic #-}
227−
228−-- #format
229−
230−instance
231− ( a ~ Maybe Format, b ~ Maybe Format
232− ) => LabelOptic "format" A_Lens Header Header a b where
233− labelOptic = #paramSchema % #format
234− {-# INLINE labelOptic #-}
235−
236−instance
237− ( a ~ Maybe Format, b ~ Maybe Format
238− ) => LabelOptic "format" A_Lens Schema Schema a b where
239− labelOptic = #paramSchema % #format
240− {-# INLINE labelOptic #-}
241−
242−instance
243− ( a ~ Maybe Format, b ~ Maybe Format
244− ) => LabelOptic "format" A_Lens NamedSchema NamedSchema a b where
245− labelOptic = #paramSchema % #format
246− {-# INLINE labelOptic #-}
247−
248−-- #items
249−
250−instance
251− ( a ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
252− , b ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
253− ) => LabelOptic "items" A_Lens Header Header a b where
254− labelOptic = #paramSchema % #items
255− {-# INLINE labelOptic #-}
256−
257−instance
258− ( a ~ Maybe (SwaggerItems 'SwaggerKindSchema)
259− , b ~ Maybe (SwaggerItems 'SwaggerKindSchema)
260− ) => LabelOptic "items" A_Lens Schema Schema a b where
261− labelOptic = #paramSchema % #items
262− {-# INLINE labelOptic #-}
263−
264−instance
265− ( a ~ Maybe (SwaggerItems 'SwaggerKindSchema)
266− , b ~ Maybe (SwaggerItems 'SwaggerKindSchema)
267− ) => LabelOptic "items" A_Lens NamedSchema NamedSchema a b where
268− labelOptic = #paramSchema % #items
269− {-# INLINE labelOptic #-}
270−
271−-- #maximum
272−
273−instance
274− ( a ~ Maybe Scientific, b ~ Maybe Scientific
275− ) => LabelOptic "maximum" A_Lens Header Header a b where
276− labelOptic = #paramSchema % #maximum
277− {-# INLINE labelOptic #-}
278−
279−instance
280− ( a ~ Maybe Scientific, b ~ Maybe Scientific
281− ) => LabelOptic "maximum" A_Lens Schema Schema a b where
282− labelOptic = #paramSchema % #maximum
283− {-# INLINE labelOptic #-}
284−
285−instance
286− ( a ~ Maybe Scientific, b ~ Maybe Scientific
287− ) => LabelOptic "maximum" A_Lens NamedSchema NamedSchema a b where
288− labelOptic = #paramSchema % #maximum
289− {-# INLINE labelOptic #-}
290−
291−-- #exclusiveMaximum
292−
293−instance
294− ( a ~ Maybe Bool, b ~ Maybe Bool
295− ) => LabelOptic "exclusiveMaximum" A_Lens Header Header a b where
296− labelOptic = #paramSchema % #exclusiveMaximum
297− {-# INLINE labelOptic #-}
298−
299−instance
300− ( a ~ Maybe Bool, b ~ Maybe Bool
301− ) => LabelOptic "exclusiveMaximum" A_Lens Schema Schema a b where
302− labelOptic = #paramSchema % #exclusiveMaximum
303− {-# INLINE labelOptic #-}
304−
305−instance
306− ( a ~ Maybe Bool, b ~ Maybe Bool
307− ) => LabelOptic "exclusiveMaximum" A_Lens NamedSchema NamedSchema a b where
308− labelOptic = #paramSchema % #exclusiveMaximum
309− {-# INLINE labelOptic #-}
310−
311−-- #minimum
312−
313−instance
314− ( a ~ Maybe Scientific, b ~ Maybe Scientific
315− ) => LabelOptic "minimum" A_Lens Header Header a b where
316− labelOptic = #paramSchema % #minimum
317− {-# INLINE labelOptic #-}
318−
319−instance
320− ( a ~ Maybe Scientific, b ~ Maybe Scientific
321− ) => LabelOptic "minimum" A_Lens Schema Schema a b where
322− labelOptic = #paramSchema % #minimum
323− {-# INLINE labelOptic #-}
324−
325−instance
326− ( a ~ Maybe Scientific, b ~ Maybe Scientific
327− ) => LabelOptic "minimum" A_Lens NamedSchema NamedSchema a b where
328− labelOptic = #paramSchema % #minimum
329− {-# INLINE labelOptic #-}
330−
331−-- #exclusiveMinimum
332−
333−instance
334− ( a ~ Maybe Bool, b ~ Maybe Bool
335− ) => LabelOptic "exclusiveMinimum" A_Lens Header Header a b where
336− labelOptic = #paramSchema % #exclusiveMinimum
337− {-# INLINE labelOptic #-}
338−
339−instance
340− ( a ~ Maybe Bool, b ~ Maybe Bool
341− ) => LabelOptic "exclusiveMinimum" A_Lens Schema Schema a b where
342− labelOptic = #paramSchema % #exclusiveMinimum
343− {-# INLINE labelOptic #-}
344−
345−instance
346− ( a ~ Maybe Bool, b ~ Maybe Bool
347− ) => LabelOptic "exclusiveMinimum" A_Lens NamedSchema NamedSchema a b where
348− labelOptic = #paramSchema % #exclusiveMinimum
349− {-# INLINE labelOptic #-}
350−
351−-- #maxLength
352−
353−instance
354− ( a ~ Maybe Integer, b ~ Maybe Integer
355− ) => LabelOptic "maxLength" A_Lens Header Header a b where
356− labelOptic = #paramSchema % #maxLength
357− {-# INLINE labelOptic #-}
358−
359−instance
360− ( a ~ Maybe Integer, b ~ Maybe Integer
361− ) => LabelOptic "maxLength" A_Lens Schema Schema a b where
362− labelOptic = #paramSchema % #maxLength
363− {-# INLINE labelOptic #-}
364−
365−instance
366− ( a ~ Maybe Integer, b ~ Maybe Integer
367− ) => LabelOptic "maxLength" A_Lens NamedSchema NamedSchema a b where
368− labelOptic = #paramSchema % #maxLength
369− {-# INLINE labelOptic #-}
370−
371−-- #minLength
372−
373−instance
374− ( a ~ Maybe Integer, b ~ Maybe Integer
375− ) => LabelOptic "minLength" A_Lens Header Header a b where
376− labelOptic = #paramSchema % #minLength
377− {-# INLINE labelOptic #-}
378−
379−instance
380− ( a ~ Maybe Integer, b ~ Maybe Integer
381− ) => LabelOptic "minLength" A_Lens Schema Schema a b where
382− labelOptic = #paramSchema % #minLength
383− {-# INLINE labelOptic #-}
384−
385−instance
386− ( a ~ Maybe Integer, b ~ Maybe Integer
387− ) => LabelOptic "minLength" A_Lens NamedSchema NamedSchema a b where
388− labelOptic = #paramSchema % #minLength
389− {-# INLINE labelOptic #-}
390−
391−-- #pattern
392−
393−instance
394− ( a ~ Maybe Text, b ~ Maybe Text
395− ) => LabelOptic "pattern" A_Lens Header Header a b where
396− labelOptic = #paramSchema % #pattern
397− {-# INLINE labelOptic #-}
398−
399−instance
400− ( a ~ Maybe Text, b ~ Maybe Text
401− ) => LabelOptic "pattern" A_Lens Schema Schema a b where
402− labelOptic = #paramSchema % #pattern
403− {-# INLINE labelOptic #-}
404−
405−instance
406− ( a ~ Maybe Text, b ~ Maybe Text
407− ) => LabelOptic "pattern" A_Lens NamedSchema NamedSchema a b where
408− labelOptic = #paramSchema % #pattern
409− {-# INLINE labelOptic #-}
410−
411−-- #maxItems
412−
413−instance
414− ( a ~ Maybe Integer, b ~ Maybe Integer
415− ) => LabelOptic "maxItems" A_Lens Header Header a b where
416− labelOptic = #paramSchema % #maxItems
417− {-# INLINE labelOptic #-}
418−
419−instance
420− ( a ~ Maybe Integer, b ~ Maybe Integer
421− ) => LabelOptic "maxItems" A_Lens Schema Schema a b where
422− labelOptic = #paramSchema % #maxItems
423− {-# INLINE labelOptic #-}
424−
425−instance
426− ( a ~ Maybe Integer, b ~ Maybe Integer
427− ) => LabelOptic "maxItems" A_Lens NamedSchema NamedSchema a b where
428− labelOptic = #paramSchema % #maxItems
429− {-# INLINE labelOptic #-}
430−
431−-- #minItems
432−
433−instance
434− ( a ~ Maybe Integer, b ~ Maybe Integer
435− ) => LabelOptic "minItems" A_Lens Header Header a b where
436− labelOptic = #paramSchema % #minItems
437− {-# INLINE labelOptic #-}
438−
439−instance
440− ( a ~ Maybe Integer, b ~ Maybe Integer
441− ) => LabelOptic "minItems" A_Lens Schema Schema a b where
442− labelOptic = #paramSchema % #minItems
443− {-# INLINE labelOptic #-}
444−
445−instance
446− ( a ~ Maybe Integer, b ~ Maybe Integer
447− ) => LabelOptic "minItems" A_Lens NamedSchema NamedSchema a b where
448− labelOptic = #paramSchema % #minItems
449− {-# INLINE labelOptic #-}
450−
451−-- #uniqueItems
452−
453−instance
454− ( a ~ Maybe Bool, b ~ Maybe Bool
455− ) => LabelOptic "uniqueItems" A_Lens Header Header a b where
456− labelOptic = #paramSchema % #uniqueItems
457− {-# INLINE labelOptic #-}
458−
459−instance
460− ( a ~ Maybe Bool, b ~ Maybe Bool
461− ) => LabelOptic "uniqueItems" A_Lens Schema Schema a b where
462− labelOptic = #paramSchema % #uniqueItems
463− {-# INLINE labelOptic #-}
464−
465−instance
466− ( a ~ Maybe Bool, b ~ Maybe Bool
467− ) => LabelOptic "uniqueItems" A_Lens NamedSchema NamedSchema a b where
468− labelOptic = #paramSchema % #uniqueItems
469− {-# INLINE labelOptic #-}
470−
471−-- #enum
472−
473−instance
474− ( a ~ Maybe [Value], b ~ Maybe [Value]
475− ) => LabelOptic "enum" A_Lens Header Header a b where
476− labelOptic = #paramSchema % #enum
477− {-# INLINE labelOptic #-}
478−
479−instance
480− ( a ~ Maybe [Value], b ~ Maybe [Value]
481− ) => LabelOptic "enum" A_Lens Schema Schema a b where
482− labelOptic = #paramSchema % #enum
483− {-# INLINE labelOptic #-}
484−
485−instance
486− ( a ~ Maybe [Value], b ~ Maybe [Value]
487− ) => LabelOptic "enum" A_Lens NamedSchema NamedSchema a b where
488− labelOptic = #paramSchema % #enum
489− {-# INLINE labelOptic #-}
490−
491−-- #multipleOf
492−
493−instance
494− ( a ~ Maybe Scientific, b ~ Maybe Scientific
495− ) => LabelOptic "multipleOf" A_Lens Header Header a b where
496− labelOptic = #paramSchema % #multipleOf
497− {-# INLINE labelOptic #-}
498−
499−instance
500− ( a ~ Maybe Scientific, b ~ Maybe Scientific
501− ) => LabelOptic "multipleOf" A_Lens Schema Schema a b where
502− labelOptic = #paramSchema % #multipleOf
503− {-# INLINE labelOptic #-}
504−
505−instance
506− ( a ~ Maybe Scientific, b ~ Maybe Scientific
507− ) => LabelOptic "multipleOf" A_Lens NamedSchema NamedSchema a b where
508− labelOptic = #paramSchema % #multipleOf
509− {-# INLINE labelOptic #-}
stack.yaml+1−1
@@ -1,4 +1,4 @@
1−resolver: lts-15.1
1+resolver: lts-16.31
2packages:
2packages:
3- '.'
3- '.'
4extra-deps:
4extra-deps:
test/Data/Swagger/CommonTestTypes.hs → test/Data/OpenApi/CommonTestTypes.hsrenamed+436−100
@@ -2,7 +2,7 @@
2{-# LANGUAGE QuasiQuotes #-}
2{-# LANGUAGE QuasiQuotes #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4
4
5−module Data.Swagger.CommonTestTypes where
5+module Data.OpenApi.CommonTestTypes where
6
6
7import Prelude ()
7import Prelude ()
8import Prelude.Compat
8import Prelude.Compat
@@ -18,7 +18,7 @@ import qualified Data.Text as Text
18import Data.Word
18import Data.Word
19import GHC.Generics
19import GHC.Generics
20
20
21−import Data.Swagger
21+import Data.OpenApi
22
22
23-- ========================================================================
23-- ========================================================================
24-- Unit type
24-- Unit type
@@ -88,7 +88,7 @@ paintSchemaJSON = [aesonQQ|
88 {
88 {
89 "color":
89 "color":
90 {
90 {
91− "$ref": "#/definitions/Color"
91+ "$ref": "#/components/schemas/Color"
92 }
92 }
93 },
93 },
94 "required": ["color"]
94 "required": ["color"]
@@ -181,7 +181,7 @@ userGroupSchemaJSON :: Value
181userGroupSchemaJSON = [aesonQQ|
181userGroupSchemaJSON = [aesonQQ|
182{
182{
183 "type": "array",
183 "type": "array",
184− "items": { "$ref": "#/definitions/UserId" },
184+ "items": { "$ref": "#/components/schemas/UserId" },
185 "uniqueItems": true
185 "uniqueItems": true
186}
186}
187|]
187|]
@@ -228,7 +228,7 @@ playerSchemaJSON = [aesonQQ|
228 {
228 {
229 "position":
229 "position":
230 {
230 {
231− "$ref": "#/definitions/Point"
231+ "$ref": "#/components/schemas/Point"
232 }
232 }
233 },
233 },
234 "required": ["position"]
234 "required": ["position"]
@@ -250,7 +250,7 @@ playersSchemaJSON = [aesonQQ|
250 {
250 {
251 "position":
251 "position":
252 {
252 {
253− "$ref": "#/definitions/Point"
253+ "$ref": "#/components/schemas/Point"
254 }
254 }
255 },
255 },
256 "required": ["position"]
256 "required": ["position"]
@@ -271,106 +271,192 @@ instance ToSchema Character
271characterSchemaJSON :: Value
271characterSchemaJSON :: Value
272characterSchemaJSON = [aesonQQ|
272characterSchemaJSON = [aesonQQ|
273{
273{
274− "type": "object",
274+ "oneOf": [
275− "properties":
276 {
275 {
277− "PC": { "$ref": "#/definitions/Player" },
276+ "required": [
278− "NPC":
277+ "tag",
279− {
278+ "contents"
279+ ],
280− "type": "object",
280+ "type": "object",
281− "properties":
281+ "properties": {
282− {
282+ "tag": {
283− "npcName": { "type": "string" },
283+ "type": "string",
284− "npcPosition": { "$ref": "#/definitions/Point" }
284+ "enum": [
285+ "PC"
286+ ]
285− },
287+ },
286− "required": ["npcName", "npcPosition"]
288+ "contents": {
289+ "$ref": "#/components/schemas/Player"
287 }
290 }
291+ }
288 },
292 },
289− "maxProperties": 1,
293+ {
290− "minProperties": 1
294+ "required": [
295+ "npcName",
296+ "npcPosition",
297+ "tag"
298+ ],
299+ "type": "object",
300+ "properties": {
301+ "tag": {
302+ "type": "string",
303+ "enum": [
304+ "NPC"
305+ ]
306+ },
307+ "npcPosition": {
308+ "$ref": "#/components/schemas/Point"
309+ },
310+ "npcName": {
311+ "type": "string"
312+ }
313+ }
314+ }
315+ ],
316+ "type": "object"
291}
317}
318+
292|]
319|]
293
320
294characterInlinedSchemaJSON :: Value
321characterInlinedSchemaJSON :: Value
295characterInlinedSchemaJSON = [aesonQQ|
322characterInlinedSchemaJSON = [aesonQQ|
296{
323{
297− "type": "object",
324+ "oneOf": [
298− "properties":
299 {
325 {
326+ "required": [
327+ "tag",
328+ "contents"
329+ ],
330+ "type": "object",
331+ "properties": {
332+ "tag": {
333+ "type": "string",
334+ "enum": [
300− "PC":
335+ "PC"
301− {
336+ ]
337+ },
338+ "contents": {
339+ "required": [
340+ "position"
341+ ],
302 "type": "object",
342 "type": "object",
303− "properties":
343+ "properties": {
304− {
305− "position":
344+ "position": {
306− {
307− "type": "object",
308− "properties":
309− {
310− "x": { "type": "number", "format": "double" },
311− "y": { "type": "number", "format": "double" }
312− },
313− "required": ["x", "y"]
345+ "required": [
346+ "x",
347+ "y"
348+ ],
349+ "type": "object",
350+ "properties": {
351+ "x": {
352+ "format": "double",
353+ "type": "number"
354+ },
355+ "y": {
356+ "format": "double",
357+ "type": "number"
314 }
358 }
359+ }
315− },
360+ }
361+ }
362+ }
363+ }
364+ },
365+ {
316− "required": ["position"]
366+ "required": [
367+ "npcName",
368+ "npcPosition",
369+ "tag"
370+ ],
371+ "type": "object",
372+ "properties": {
373+ "tag": {
374+ "type": "string",
375+ "enum": [
376+ "NPC"
377+ ]
317 },
378 },
318− "NPC":
379+ "npcPosition": {
319− {
380+ "required": [
381+ "x",
382+ "y"
383+ ],
320 "type": "object",
384 "type": "object",
321− "properties":
385+ "properties": {
322− {
386+ "x": {
323− "npcName": { "type": "string" },
324− "npcPosition":
325− {
326− "type": "object",
327− "properties":
328− {
329− "x": { "type": "number", "format": "double" },
387+ "format": "double",
330− "y": { "type": "number", "format": "double" }
388+ "type": "number"
331− },
332− "required": ["x", "y"]
333− }
334 },
389 },
390+ "y": {
391+ "format": "double",
392+ "type": "number"
393+ }
394+ }
395+ },
335− "required": ["npcName", "npcPosition"]
396+ "npcName": {
397+ "type": "string"
336 }
398 }
399+ }
337− },
400+ }
338− "maxProperties": 1,
401+ ],
339− "minProperties": 1
402+ "type": "object"
340}
403}
341|]
404|]
342
405
343characterInlinedPlayerSchemaJSON :: Value
406characterInlinedPlayerSchemaJSON :: Value
344characterInlinedPlayerSchemaJSON = [aesonQQ|
407characterInlinedPlayerSchemaJSON = [aesonQQ|
345{
408{
346− "type": "object",
409+ "oneOf": [
347− "properties":
348 {
410 {
349− "PC":
411+ "required": [
350− {
412+ "tag",
413+ "contents"
414+ ],
351− "type": "object",
415+ "type": "object",
352− "properties":
416+ "properties": {
353− {
417+ "tag": {
354− "position":
418+ "type": "string",
355− {
419+ "enum": [
356− "$ref": "#/definitions/Point"
420+ "PC"
357− }
421+ ]
358− },
359− "required": ["position"]
360 },
422 },
361− "NPC":
423+ "contents": {
362− {
424+ "required": [
425+ "position"
426+ ],
363 "type": "object",
427 "type": "object",
364− "properties":
428+ "properties": {
365− {
429+ "position": {
366− "npcName": { "type": "string" },
430+ "$ref": "#/components/schemas/Point"
367− "npcPosition": { "$ref": "#/definitions/Point" }
368− },
431+ }
369− "required": ["npcName", "npcPosition"]
432+ }
370 }
433 }
434+ }
371 },
435 },
372− "maxProperties": 1,
436+ {
373− "minProperties": 1
437+ "required": [
438+ "npcName",
439+ "npcPosition",
440+ "tag"
441+ ],
442+ "type": "object",
443+ "properties": {
444+ "tag": {
445+ "type": "string",
446+ "enum": [
447+ "NPC"
448+ ]
449+ },
450+ "npcPosition": {
451+ "$ref": "#/components/schemas/Point"
452+ },
453+ "npcName": {
454+ "type": "string"
455+ }
456+ }
457+ }
458+ ],
459+ "type": "object"
374}
460}
375|]
461|]
376
462
@@ -482,7 +568,7 @@ myRoseTreeSchemaJSON = [aesonQQ|
482 "type": "array",
568 "type": "array",
483 "items":
569 "items":
484 {
570 {
485− "$ref": "#/definitions/RoseTree"
571+ "$ref": "#/components/schemas/RoseTree"
486 }
572 }
487 }
573 }
488 },
574 },
@@ -511,7 +597,7 @@ myRoseTreeSchemaJSON' = [aesonQQ|
511 "type": "array",
597 "type": "array",
512 "items":
598 "items":
513 {
599 {
514− "$ref": "#/definitions/myrosetree'"
600+ "$ref": "#/components/schemas/myrosetree'"
515 }
601 }
516 }
602 }
517 },
603 },
@@ -542,42 +628,169 @@ data Light
542 deriving (Generic)
628 deriving (Generic)
543
629
544instance ToSchema Light where
630instance ToSchema Light where
545− declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
631+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
546 { unwrapUnaryRecords = True }
632 { unwrapUnaryRecords = True }
547
633
548lightSchemaJSON :: Value
634lightSchemaJSON :: Value
549lightSchemaJSON = [aesonQQ|
635lightSchemaJSON = [aesonQQ|
550{
636{
551− "type": "object",
637+ "oneOf": [
552− "properties":
553 {
638 {
554− "NoLight": { "type": "array", "items": {}, "maxItems": 0, "example": [] },
639+ "required": [
640+ "tag"
641+ ],
642+ "type": "object",
643+ "properties": {
644+ "tag": {
645+ "type": "string",
646+ "enum": [
647+ "NoLight"
648+ ]
649+ }
650+ }
651+ },
652+ {
653+ "required": [
654+ "tag",
655+ "contents"
656+ ],
657+ "type": "object",
658+ "properties": {
659+ "tag": {
660+ "type": "string",
661+ "enum": [
662+ "LightFreq"
663+ ]
664+ },
665+ "contents": {
666+ "format": "double",
555− "LightFreq": { "type": "number", "format": "double" },
667+ "type": "number"
668+ }
669+ }
670+ },
671+ {
672+ "required": [
673+ "tag",
674+ "contents"
675+ ],
676+ "type": "object",
677+ "properties": {
678+ "tag": {
679+ "type": "string",
680+ "enum": [
556− "LightColor": { "$ref": "#/definitions/Color" },
681+ "LightColor"
557− "LightWaveLength": { "type": "number", "format": "double" }
682+ ]
683+ },
684+ "contents": {
685+ "$ref": "#/components/schemas/Color"
686+ }
687+ }
558 },
688 },
559− "maxProperties": 1,
689+ {
560− "minProperties": 1
690+ "required": [
691+ "waveLength",
692+ "tag"
693+ ],
694+ "type": "object",
695+ "properties": {
696+ "tag": {
697+ "type": "string",
698+ "enum": [
699+ "LightWaveLength"
700+ ]
701+ },
702+ "waveLength": {
703+ "format": "double",
704+ "type": "number"
705+ }
706+ }
707+ }
708+ ],
709+ "type": "object"
561}
710}
562|]
711|]
563
712
564lightInlinedSchemaJSON :: Value
713lightInlinedSchemaJSON :: Value
565lightInlinedSchemaJSON = [aesonQQ|
714lightInlinedSchemaJSON = [aesonQQ|
566{
715{
567− "type": "object",
716+ "oneOf": [
568− "properties":
569 {
717 {
570− "NoLight": { "type": "array", "items": {}, "maxItems": 0, "example": [] },
718+ "required": [
571− "LightFreq": { "type": "number", "format": "double" },
719+ "tag"
572− "LightColor":
720+ ],
573− {
721+ "type": "object",
722+ "properties": {
723+ "tag": {
574 "type": "string",
724 "type": "string",
575− "enum": ["Red", "Green", "Blue"]
725+ "enum": [
726+ "NoLight"
727+ ]
728+ }
729+ }
730+ },
731+ {
732+ "required": [
733+ "tag",
734+ "contents"
735+ ],
736+ "type": "object",
737+ "properties": {
738+ "tag": {
739+ "type": "string",
740+ "enum": [
741+ "LightFreq"
742+ ]
743+ },
744+ "contents": {
745+ "format": "double",
746+ "type": "number"
747+ }
748+ }
749+ },
750+ {
751+ "required": [
752+ "tag",
753+ "contents"
754+ ],
755+ "type": "object",
756+ "properties": {
757+ "tag": {
758+ "type": "string",
759+ "enum": [
760+ "LightColor"
761+ ]
576 },
762 },
577− "LightWaveLength": { "type": "number", "format": "double" }
763+ "contents": {
764+ "type": "string",
765+ "enum": [
766+ "Red",
767+ "Green",
768+ "Blue"
769+ ]
770+ }
771+ }
578 },
772 },
579− "maxProperties": 1,
773+ {
580− "minProperties": 1
774+ "required": [
775+ "waveLength",
776+ "tag"
777+ ],
778+ "type": "object",
779+ "properties": {
780+ "tag": {
781+ "type": "string",
782+ "enum": [
783+ "LightWaveLength"
784+ ]
785+ },
786+ "waveLength": {
787+ "format": "double",
788+ "type": "number"
789+ }
790+ }
791+ }
792+ ],
793+ "type": "object"
581}
794}
582|]
795|]
583
796
@@ -650,6 +863,129 @@ singleMaybeFieldSchemaJSON = [aesonQQ|
650}
863}
651|]
864|]
652
865
866+-- ========================================================================
867+-- Natural Language (single field data with recursive fields)
868+-- ========================================================================
869+
870+data Predicate
871+ = PredicateNoun Noun
872+ | PredicateOmitted Omitted
873+ deriving (Eq, Ord, Read, Show, Generic)
874+instance ToJSON Predicate
875+instance ToSchema Predicate
876+
877+data Noun
878+ = Noun
879+ { nounSurf :: LangWord
880+ , nounModify :: [Modifier]
881+ }
882+ deriving (Eq, Ord, Read, Show, Generic)
883+instance ToJSON Noun
884+instance ToSchema Noun
885+
886+data LangWord
887+ = LangWord
888+ { langWordSurf :: String
889+ , langWordBase :: String
890+ }
891+ deriving (Eq, Ord, Read, Show, Generic)
892+instance ToJSON LangWord
893+instance ToSchema LangWord
894+
895+data Modifier
896+ = ModifierNoun Noun
897+ | ModifierOmitted Omitted
898+ deriving (Eq, Ord, Read, Show, Generic)
899+instance ToJSON Modifier
900+instance ToSchema Modifier
901+
902+newtype Omitted
903+ = Omitted
904+ { omittedModify :: [Modifier]
905+ }
906+ deriving (Eq, Ord, Read, Show, Generic)
907+instance ToJSON Omitted
908+instance ToSchema Omitted
909+
910+predicateSchemaDeclareJSON :: Value
911+predicateSchemaDeclareJSON = [aesonQQ|
912+[
913+ {
914+ "Predicate": {
915+ "oneOf": [
916+ {
917+ "properties": {
918+ "contents": { "$ref": "#/components/schemas/Noun" },
919+ "tag": { "enum": ["PredicateNoun"], "type": "string" }
920+ },
921+ "required": ["tag", "contents"],
922+ "type": "object"
923+ },
924+ {
925+ "properties": {
926+ "contents": { "$ref": "#/components/schemas/Omitted" },
927+ "tag": { "enum": ["PredicateOmitted"], "type": "string" }
928+ },
929+ "required": ["tag", "contents"],
930+ "type": "object"
931+ }
932+ ],
933+ "type": "object"
934+ },
935+ "Noun": {
936+ "properties": {
937+ "nounModify": {
938+ "items": { "$ref": "#/components/schemas/Modifier" },
939+ "type": "array"
940+ },
941+ "nounSurf": { "$ref": "#/components/schemas/LangWord" }
942+ },
943+ "required": ["nounSurf", "nounModify"],
944+ "type": "object"
945+ },
946+ "LangWord": {
947+ "properties": {
948+ "langWordBase": { "type": "string" },
949+ "langWordSurf": { "type": "string" }
950+ },
951+ "required": ["langWordSurf", "langWordBase"],
952+ "type": "object"
953+ },
954+ "Modifier": {
955+ "oneOf": [
956+ {
957+ "properties": {
958+ "contents": { "$ref": "#/components/schemas/Noun" },
959+ "tag": { "enum": ["ModifierNoun"], "type": "string" }
960+ },
961+ "required": ["tag", "contents"],
962+ "type": "object"
963+ },
964+ {
965+ "properties": {
966+ "contents": { "$ref": "#/components/schemas/Omitted" },
967+ "tag": { "enum": ["ModifierOmitted"], "type": "string" }
968+ },
969+ "required": ["tag", "contents"],
970+ "type": "object"
971+ }
972+ ],
973+ "type": "object"
974+ },
975+ "Omitted": {
976+ "properties": {
977+ "omittedModify": {
978+ "items": { "$ref": "#/components/schemas/Modifier" },
979+ "type": "array"
980+ }
981+ },
982+ "required": ["omittedModify"],
983+ "type": "object"
984+ }
985+ },
986+ { "$ref": "#/components/schemas/Predicate" }
987+]
988+|]
653
989
654-- ========================================================================
990-- ========================================================================
655-- TimeOfDay
991-- TimeOfDay
test/Data/Swagger/ParamSchemaSpec.hs → test/Data/OpenApi/ParamSchemaSpec.hsrenamed+4−5
@@ -4,7 +4,7 @@
4{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE QuasiQuotes #-}
5{-# LANGUAGE QuasiQuotes #-}
6{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE OverloadedStrings #-}
7−module Data.Swagger.ParamSchemaSpec where
7+module Data.OpenApi.ParamSchemaSpec where
8
8
9import Data.Aeson
9import Data.Aeson
10import Data.Aeson.QQ.Simple
10import Data.Aeson.QQ.Simple
@@ -12,10 +12,9 @@ import Data.Char
12import Data.Proxy
12import Data.Proxy
13import GHC.Generics
13import GHC.Generics
14
14
15−import Data.Swagger
15+import Data.OpenApi
16−import Data.Swagger.Internal (SwaggerKind(..))
17
16
18−import Data.Swagger.CommonTestTypes
17+import Data.OpenApi.CommonTestTypes
19import SpecCommon
18import SpecCommon
20import Test.Hspec
19import Test.Hspec
21import Data.Time.LocalTime
20import Data.Time.LocalTime
@@ -23,7 +22,7 @@ import Data.Time.LocalTime
23import qualified Data.HashMap.Strict as HM
22import qualified Data.HashMap.Strict as HM
24
23
25checkToParamSchema :: ToParamSchema a => Proxy a -> Value -> Spec
24checkToParamSchema :: ToParamSchema a => Proxy a -> Value -> Spec
26−checkToParamSchema proxy js = (toParamSchema proxy :: ParamSchema ('SwaggerKindNormal Param)) <=> js
25+checkToParamSchema proxy js = (toParamSchema proxy :: Schema) <=> js
27
26
28spec :: Spec
27spec :: Spec
29spec = do
28spec = do
test/Data/Swagger/Schema/GeneratorSpec.hs → test/Data/OpenApi/Schema/GeneratorSpec.hsrenamed+7−7
@@ -1,13 +1,13 @@
1{-# LANGUAGE OverloadedLists #-}
1{-# LANGUAGE OverloadedLists #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE PackageImports #-}
4−module Data.Swagger.Schema.GeneratorSpec where
4+module Data.OpenApi.Schema.GeneratorSpec where
5
5
6import Prelude ()
6import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9−import Data.Swagger
9+import Data.OpenApi
10−import Data.Swagger.Schema.Generator
10+import Data.OpenApi.Schema.Generator
11
11
12import Control.Lens.Operators
12import Control.Lens.Operators
13import Data.Aeson
13import Data.Aeson
@@ -103,7 +103,7 @@ instance FromJSON WrongType where
103instance ToSchema WrongType where
103instance ToSchema WrongType where
104 declareNamedSchema _ = return . NamedSchema (Just "WrongType") $
104 declareNamedSchema _ = return . NamedSchema (Just "WrongType") $
105 mempty
105 mempty
106− & type_ ?~ SwaggerObject
106+ & type_ ?~ OpenApiObject
107
107
108
108
109data MissingRequired = MissingRequired
109data MissingRequired = MissingRequired
@@ -123,7 +123,7 @@ instance ToSchema MissingRequired where
123 boolSchema <- declareSchemaRef (Proxy :: Proxy Bool)
123 boolSchema <- declareSchemaRef (Proxy :: Proxy Bool)
124 return . NamedSchema (Just "MissingRequired") $
124 return . NamedSchema (Just "MissingRequired") $
125 mempty
125 mempty
126− & type_ ?~ SwaggerObject
126+ & type_ ?~ OpenApiObject
127 & properties .~ [("propA", stringSchema)
127 & properties .~ [("propA", stringSchema)
128 ,("propB", boolSchema)
128 ,("propB", boolSchema)
129 ]
129 ]
@@ -145,7 +145,7 @@ instance ToSchema MissingProperty where
145 stringSchema <- declareSchemaRef (Proxy :: Proxy String)
145 stringSchema <- declareSchemaRef (Proxy :: Proxy String)
146 return . NamedSchema (Just "MissingProperty") $
146 return . NamedSchema (Just "MissingProperty") $
147 mempty
147 mempty
148− & type_ ?~ SwaggerObject
148+ & type_ ?~ OpenApiObject
149 & properties .~ [("propC", stringSchema)]
149 & properties .~ [("propC", stringSchema)]
150 & required .~ ["propC"]
150 & required .~ ["propC"]
151
151
@@ -163,6 +163,6 @@ instance ToSchema WrongPropType where
163 boolSchema <- declareSchemaRef (Proxy :: Proxy Bool)
163 boolSchema <- declareSchemaRef (Proxy :: Proxy Bool)
164 return . NamedSchema (Just "WrongPropType") $
164 return . NamedSchema (Just "WrongPropType") $
165 mempty
165 mempty
166− & type_ ?~ SwaggerObject
166+ & type_ ?~ OpenApiObject
167 & properties .~ [("propE", boolSchema)]
167 & properties .~ [("propE", boolSchema)]
168 & required .~ ["propE"]
168 & required .~ ["propE"]
test/Data/Swagger/Schema/ValidationSpec.hs → test/Data/OpenApi/Schema/ValidationSpec.hsrenamed+56−9
@@ -1,13 +1,18 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE DeriveGeneric #-}
2{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
6−module Data.Swagger.Schema.ValidationSpec where
7+module Data.OpenApi.Schema.ValidationSpec where
7
8
8import Control.Applicative
9import Control.Applicative
9import Control.Lens ((&), (.~), (?~))
10import Control.Lens ((&), (.~), (?~))
10import Data.Aeson
11import Data.Aeson
12+#if MIN_VERSION_aeson(2,0,0)
13+import qualified Data.Aeson.Key as Key
14+import qualified Data.Aeson.KeyMap as KeyMap
15+#endif
11import Data.Aeson.Types
16import Data.Aeson.Types
12import Data.Hashable (Hashable)
17import Data.Hashable (Hashable)
13import Data.HashMap.Strict (HashMap)
18import Data.HashMap.Strict (HashMap)
@@ -28,8 +33,9 @@ import Data.Version (Version)
28import Data.Word
33import Data.Word
29import GHC.Generics
34import GHC.Generics
30
35
31−import Data.Swagger
36+import Data.OpenApi
32−import Data.Swagger.Declare
37+import Data.OpenApi.Declare
38+import Data.OpenApi.Aeson.Compat (stringToKey)
33
39
34import Test.Hspec
40import Test.Hspec
35import Test.Hspec.QuickCheck
41import Test.Hspec.QuickCheck
@@ -91,6 +97,8 @@ spec = do
91 prop "Paint" $ shouldValidate (Proxy :: Proxy Paint)
97 prop "Paint" $ shouldValidate (Proxy :: Proxy Paint)
92 prop "MyRoseTree" $ shouldValidate (Proxy :: Proxy MyRoseTree)
98 prop "MyRoseTree" $ shouldValidate (Proxy :: Proxy MyRoseTree)
93 prop "Light" $ shouldValidate (Proxy :: Proxy Light)
99 prop "Light" $ shouldValidate (Proxy :: Proxy Light)
100+ prop "Light TaggedObject" $ shouldValidate (Proxy :: Proxy LightTaggedObject)
101+ prop "Light UntaggedValue" $ shouldValidate (Proxy :: Proxy LightUntaggedValue)
94 prop "ButtonImages" $ shouldValidate (Proxy :: Proxy ButtonImages)
102 prop "ButtonImages" $ shouldValidate (Proxy :: Proxy ButtonImages)
95 prop "Version" $ shouldValidate (Proxy :: Proxy Version)
103 prop "Version" $ shouldValidate (Proxy :: Proxy Version)
96 prop "FreeForm" $ shouldValidate (Proxy :: Proxy FreeForm)
104 prop "FreeForm" $ shouldValidate (Proxy :: Proxy FreeForm)
@@ -122,9 +130,9 @@ instance Arbitrary Person where
122
130
123invalidPersonToJSON :: Person -> Value
131invalidPersonToJSON :: Person -> Value
124invalidPersonToJSON Person{..} = object
132invalidPersonToJSON Person{..} = object
125− [ T.pack "personName" .= toJSON name
133+ [ stringToKey "personName" .= toJSON name
126− , T.pack "personPhone" .= toJSON phone
134+ , stringToKey "personPhone" .= toJSON phone
127− , T.pack "personEmail" .= toJSON email
135+ , stringToKey "personEmail" .= toJSON email
128 ]
136 ]
129
137
130-- ========================================================================
138-- ========================================================================
@@ -188,10 +196,10 @@ instance Arbitrary MyRoseTree where
188data Light = NoLight | LightFreq Double | LightColor Color deriving (Show, Generic)
196data Light = NoLight | LightFreq Double | LightColor Color deriving (Show, Generic)
189
197
190instance ToSchema Light where
198instance ToSchema Light where
191− declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
199+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions { Data.OpenApi.sumEncoding = ObjectWithSingleField }
192
200
193instance ToJSON Light where
201instance ToJSON Light where
194− toJSON = genericToJSON defaultOptions { sumEncoding = ObjectWithSingleField }
202+ toJSON = genericToJSON defaultOptions { Data.Aeson.Types.sumEncoding = ObjectWithSingleField }
195
203
196instance Arbitrary Light where
204instance Arbitrary Light where
197 arbitrary = oneof
205 arbitrary = oneof
@@ -203,6 +211,34 @@ instance Arbitrary Light where
203invalidLightToJSON :: Light -> Value
211invalidLightToJSON :: Light -> Value
204invalidLightToJSON = genericToJSON defaultOptions
212invalidLightToJSON = genericToJSON defaultOptions
205
213
214+-- Check all SumEncoding flavors.
215+
216+newtype LightTaggedObject = LightTaggedObject Light
217+ deriving (Show)
218+
219+instance ToJSON LightTaggedObject where
220+ toJSON (LightTaggedObject light) = genericToJSON defaultOptions { Data.Aeson.Types.sumEncoding = defaultTaggedObject } light
221+
222+instance ToSchema LightTaggedObject where
223+ declareNamedSchema _ =
224+ genericDeclareNamedSchema defaultSchemaOptions { Data.OpenApi.sumEncoding = defaultTaggedObject } (Proxy :: Proxy Light)
225+
226+instance Arbitrary LightTaggedObject where
227+ arbitrary = LightTaggedObject <$> arbitrary
228+
229+newtype LightUntaggedValue = LightUntaggedValue Light
230+ deriving (Show)
231+
232+instance ToJSON LightUntaggedValue where
233+ toJSON (LightUntaggedValue light) = genericToJSON defaultOptions { Data.Aeson.Types.sumEncoding = UntaggedValue } light
234+
235+instance ToSchema LightUntaggedValue where
236+ declareNamedSchema _ =
237+ genericDeclareNamedSchema defaultSchemaOptions { Data.OpenApi.sumEncoding = UntaggedValue } (Proxy :: Proxy Light)
238+
239+instance Arbitrary LightUntaggedValue where
240+ arbitrary = LightUntaggedValue <$> arbitrary
241+
206-- ========================================================================
242-- ========================================================================
207-- ButtonImages (bounded enum key mapping)
243-- ButtonImages (bounded enum key mapping)
208-- ========================================================================
244-- ========================================================================
@@ -247,7 +283,7 @@ instance ToJSON FreeForm where
247
283
248instance ToSchema FreeForm where
284instance ToSchema FreeForm where
249 declareNamedSchema _ = pure $ NamedSchema (Just $ T.pack "FreeForm") $ mempty
285 declareNamedSchema _ = pure $ NamedSchema (Just $ T.pack "FreeForm") $ mempty
250− & type_ ?~ SwaggerObject
286+ & type_ ?~ OpenApiObject
251 & additionalProperties ?~ AdditionalPropertiesAllowed True
287 & additionalProperties ?~ AdditionalPropertiesAllowed True
252
288
253instance Arbitrary FreeForm where
289instance Arbitrary FreeForm where
@@ -265,6 +301,8 @@ instance Eq ZonedTime where
265-- Arbitrary instance for Data.Aeson.Value
301-- Arbitrary instance for Data.Aeson.Value
266-- ========================================================================
302-- ========================================================================
267
303
304+-- These instances were introduces in aeson upstream in 2.0.3
305+#if !MIN_VERSION_aeson(2,0,3)
268instance Arbitrary Value where
306instance Arbitrary Value where
269 -- Weights are almost random
307 -- Weights are almost random
270 -- Uniform oneof tends not to build complex objects cause of recursive call.
308 -- Uniform oneof tends not to build complex objects cause of recursive call.
@@ -275,3 +313,12 @@ instance Arbitrary Value where
275 , (3, Number <$> arbitrary)
313 , (3, Number <$> arbitrary)
276 , (3, Bool <$> arbitrary)
314 , (3, Bool <$> arbitrary)
277 , (1, return Null) ]
315 , (1, return Null) ]
316+
317+#if MIN_VERSION_aeson(2,0,0)
318+instance Arbitrary v => Arbitrary (KeyMap.KeyMap v) where
319+ arbitrary = KeyMap.fromList <$> arbitrary
320+
321+instance Arbitrary Key.Key where
322+ arbitrary = Key.fromText <$> arbitrary
323+#endif
324+#endif
test/Data/Swagger/SchemaSpec.hs → test/Data/OpenApi/SchemaSpec.hsrenamed+15−11
@@ -2,7 +2,7 @@
2{-# LANGUAGE ScopedTypeVariables #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE TemplateHaskell #-}
3{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE QuasiQuotes #-}
4{-# LANGUAGE QuasiQuotes #-}
5−module Data.Swagger.SchemaSpec where
5+module Data.OpenApi.SchemaSpec where
6
6
7import Prelude ()
7import Prelude ()
8import Prelude.Compat
8import Prelude.Compat
@@ -14,51 +14,54 @@ import Data.Proxy
14import Data.Set (Set)
14import Data.Set (Set)
15import qualified Data.Text as Text
15import qualified Data.Text as Text
16
16
17−import Data.Swagger
17+import Data.OpenApi
18−import Data.Swagger.Declare
18+import Data.OpenApi.Declare
19
19
20−import Data.Swagger.CommonTestTypes
20+import Data.OpenApi.CommonTestTypes
21import SpecCommon
21import SpecCommon
22import Test.Hspec
22import Test.Hspec
23
23
24import qualified Data.HashMap.Strict as HM
24import qualified Data.HashMap.Strict as HM
25import Data.Time.LocalTime
25import Data.Time.LocalTime
26
26
27−checkToSchema :: ToSchema a => Proxy a -> Value -> Spec
27+checkToSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
28checkToSchema proxy js = toSchema proxy <=> js
28checkToSchema proxy js = toSchema proxy <=> js
29
29
30−checkSchemaName :: ToSchema a => Maybe String -> Proxy a -> Spec
30+checkSchemaName :: (HasCallStack, ToSchema a) => Maybe String -> Proxy a -> Spec
31checkSchemaName sname proxy =
31checkSchemaName sname proxy =
32 it ("schema name is " ++ show sname) $
32 it ("schema name is " ++ show sname) $
33 schemaName proxy `shouldBe` fmap Text.pack sname
33 schemaName proxy `shouldBe` fmap Text.pack sname
34
34
35−checkDefs :: ToSchema a => Proxy a -> [String] -> Spec
35+checkDefs :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
36checkDefs proxy names =
36checkDefs proxy names =
37 it ("uses these definitions " ++ show names) $
37 it ("uses these definitions " ++ show names) $
38 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
38 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
39 where
39 where
40 defs = execDeclare (declareNamedSchema proxy) mempty
40 defs = execDeclare (declareNamedSchema proxy) mempty
41
41
42−checkProperties :: ToSchema a => Proxy a -> [String] -> Spec
42+checkProperties :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
43checkProperties proxy names =
43checkProperties proxy names =
44 it ("has these fields in order " ++ show names) $
44 it ("has these fields in order " ++ show names) $
45 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
45 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
46 where
46 where
47 fields = toSchema proxy ^. properties
47 fields = toSchema proxy ^. properties
48
48
49−checkInlinedSchema :: ToSchema a => Proxy a -> Value -> Spec
49+checkInlinedSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
50checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
50checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
51
51
52−checkInlinedSchemas :: ToSchema a => [String] -> Proxy a -> Value -> Spec
52+checkInlinedSchemas :: (HasCallStack, ToSchema a) => [String] -> Proxy a -> Value -> Spec
53checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
53checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
54 where
54 where
55 (defs, s) = runDeclare (declareSchema proxy) mempty
55 (defs, s) = runDeclare (declareSchema proxy) mempty
56
56
57−checkInlinedRecSchema :: ToSchema a => Proxy a -> Value -> Spec
57+checkInlinedRecSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
58checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
58checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
59 where
59 where
60 (defs, s) = runDeclare (declareSchema proxy) mempty
60 (defs, s) = runDeclare (declareSchema proxy) mempty
61
61
62+checkToSchemaDeclare :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
63+checkToSchemaDeclare proxy js = runDeclare (declareSchemaRef proxy) mempty <=> js
64+
62spec :: Spec
65spec :: Spec
63spec = do
66spec = do
64 describe "Generic ToSchema" $ do
67 describe "Generic ToSchema" $ do
@@ -78,6 +81,7 @@ spec = do
78 context "UserId (non-record newtype)" $ checkToSchema (Proxy :: Proxy UserId) userIdSchemaJSON
81 context "UserId (non-record newtype)" $ checkToSchema (Proxy :: Proxy UserId) userIdSchemaJSON
79 context "Player (unary record)" $ checkToSchema (Proxy :: Proxy Player) playerSchemaJSON
82 context "Player (unary record)" $ checkToSchema (Proxy :: Proxy Player) playerSchemaJSON
80 context "SingleMaybeField (unary record with Maybe)" $ checkToSchema (Proxy :: Proxy SingleMaybeField) singleMaybeFieldSchemaJSON
83 context "SingleMaybeField (unary record with Maybe)" $ checkToSchema (Proxy :: Proxy SingleMaybeField) singleMaybeFieldSchemaJSON
84+ context "Natural Language (single field data with recursive fields)" $ checkToSchemaDeclare (Proxy :: Proxy Predicate) predicateSchemaDeclareJSON
81 context "Players (inlining schema)" $ checkToSchema (Proxy :: Proxy Players) playersSchemaJSON
85 context "Players (inlining schema)" $ checkToSchema (Proxy :: Proxy Players) playersSchemaJSON
82 context "MyRoseTree (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree) myRoseTreeSchemaJSON
86 context "MyRoseTree (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree) myRoseTreeSchemaJSON
83 context "MyRoseTree' (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree') myRoseTreeSchemaJSON'
87 context "MyRoseTree' (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree') myRoseTreeSchemaJSON'
test/Data/OpenApiSpec.hsnew file+962
@@ -0,0 +1,962 @@
1+{-# LANGUAGE OverloadedLists #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE QuasiQuotes #-}
5+module Data.OpenApiSpec where
6+
7+import Prelude ()
8+import Prelude.Compat
9+
10+import Control.Lens
11+
12+import Data.Aeson
13+import Data.Aeson.QQ.Simple
14+import Data.HashMap.Strict (HashMap)
15+import qualified Data.HashSet.InsOrd as InsOrdHS
16+import Data.Text (Text)
17+
18+import Data.OpenApi
19+import SpecCommon
20+import Test.Hspec hiding (example)
21+
22+spec :: Spec
23+spec = do
24+ describe "License Object" $ licenseExample <=> licenseExampleJSON
25+ describe "Contact Object" $ contactExample <=> contactExampleJSON
26+ describe "Info Object" $ infoExample <=> infoExampleJSON
27+ describe "Operation Object" $ operationExample <=> operationExampleJSON
28+ describe "Schema Object" $ do
29+ context "Primitive Sample" $ schemaPrimitiveExample <=> schemaPrimitiveExampleJSON
30+ context "Simple Model" $ schemaSimpleModelExample <=> schemaSimpleModelExampleJSON
31+ context "Model with Map/Dictionary Properties" $ schemaModelDictExample <=> schemaModelDictExampleJSON
32+ context "Model with Arbitrary Properties" $ schemaAdditionalExample <=> schemaAdditionalExampleJSON
33+ context "Model with Example" $ schemaWithExampleExample <=> schemaWithExampleExampleJSON
34+ describe "Definitions Object" $ definitionsExample <=> definitionsExampleJSON
35+ describe "Parameters Definition Object" $ paramsDefinitionExample <=> paramsDefinitionExampleJSON
36+ describe "Responses Definition Object" $ responsesDefinitionExample <=> responsesDefinitionExampleJSON
37+ describe "Security Definitions Object" $ securityDefinitionsExample <=> securityDefinitionsExampleJSON
38+ describe "OAuth2 Security Definitions with merged Scope" $ oAuth2SecurityDefinitionsExample <=> oAuth2SecurityDefinitionsExampleJSON
39+ describe "Composition Schema Example" $ compositionSchemaExample <=> compositionSchemaExampleJSON
40+ describe "Swagger Object" $ do
41+ context "Example with no paths" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
42+ context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
43+ context "PetStore Example" $ do
44+ it "decodes successfully" $ do
45+ fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: OpenApi) -> True; _ -> False)
46+ it "roundtrips: fmap toJSON . fromJSON" $ do
47+ (toJSON :: OpenApi -> Value) <$> fromJSON petstoreExampleJSON `shouldBe` Success petstoreExampleJSON
48+ context "Security schemes" $ do
49+ it "merged correctly" $ do
50+ let merged = oAuth2SecurityDefinitionsReadOpenApi <> oAuth2SecurityDefinitionsWriteOpenApi
51+ merged `shouldBe` oAuth2SecurityDefinitionsOpenApi
52+
53+main :: IO ()
54+main = hspec spec
55+
56+-- =======================================================================
57+-- Info object
58+-- =======================================================================
59+
60+infoExample :: Info
61+infoExample = mempty
62+ & title .~ "Swagger Sample App"
63+ & description ?~ "This is a sample server Petstore server."
64+ & termsOfService ?~ "http://swagger.io/terms/"
65+ & contact ?~ contactExample
66+ & license ?~ licenseExample
67+ & version .~ "1.0.1"
68+
69+infoExampleJSON :: Value
70+infoExampleJSON = [aesonQQ|
71+{
72+ "title": "Swagger Sample App",
73+ "description": "This is a sample server Petstore server.",
74+ "termsOfService": "http://swagger.io/terms/",
75+ "contact": {
76+ "name": "API Support",
77+ "url": "http://www.swagger.io/support",
78+ "email": "support@swagger.io"
79+ },
80+ "license": {
81+ "name": "Apache 2.0",
82+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
83+ },
84+ "version": "1.0.1"
85+}
86+|]
87+
88+-- =======================================================================
89+-- Contact object
90+-- =======================================================================
91+
92+contactExample :: Contact
93+contactExample = mempty
94+ & name ?~ "API Support"
95+ & url ?~ URL "http://www.swagger.io/support"
96+ & email ?~ "support@swagger.io"
97+
98+contactExampleJSON :: Value
99+contactExampleJSON = [aesonQQ|
100+{
101+ "name": "API Support",
102+ "url": "http://www.swagger.io/support",
103+ "email": "support@swagger.io"
104+}
105+|]
106+
107+-- =======================================================================
108+-- License object
109+-- =======================================================================
110+
111+licenseExample :: License
112+licenseExample = "Apache 2.0"
113+ & url ?~ URL "http://www.apache.org/licenses/LICENSE-2.0.html"
114+
115+licenseExampleJSON :: Value
116+licenseExampleJSON = [aesonQQ|
117+{
118+ "name": "Apache 2.0",
119+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
120+}
121+|]
122+
123+
124+-- =======================================================================
125+-- Operation object
126+-- =======================================================================
127+
128+operationExample :: Operation
129+operationExample = mempty
130+ & tags .~ InsOrdHS.fromList ["pet"]
131+ & summary ?~ "Updates a pet in the store with form data"
132+ & description ?~ ""
133+ & operationId ?~ "updatePetWithForm"
134+ & parameters .~ [Inline (mempty
135+ & name .~ "petId"
136+ & description ?~ "ID of pet that needs to be updated"
137+ & required ?~ True
138+ & in_ .~ ParamPath
139+ & schema ?~ Inline (mempty & type_ ?~ OpenApiString))]
140+ & requestBody ?~ Inline (
141+ mempty & content . at "application/x-www-form-urlencoded" ?~ (mempty & schema ?~ (Inline (mempty
142+ & properties . at "petId" ?~ Inline (mempty
143+ & description ?~ "Updated name of the pet"
144+ & type_ ?~ OpenApiString)
145+ & properties . at "status" ?~ Inline (mempty
146+ & description ?~ "Updated status of the pet"
147+ & type_ ?~ OpenApiString)))))
148+ & at 200 ?~ "Pet updated."
149+ & at 405 ?~ "Invalid input"
150+ & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
151+
152+operationExampleJSON :: Value
153+operationExampleJSON = [aesonQQ|
154+{
155+ "tags": [
156+ "pet"
157+ ],
158+ "summary": "Updates a pet in the store with form data",
159+ "description": "",
160+ "operationId": "updatePetWithForm",
161+ "parameters": [
162+ {
163+ "required": true,
164+ "schema": {
165+ "type": "string"
166+ },
167+ "in": "path",
168+ "name": "petId",
169+ "description": "ID of pet that needs to be updated"
170+ }
171+ ],
172+ "requestBody": {
173+ "content": {
174+ "application/x-www-form-urlencoded": {
175+ "schema": {
176+ "properties": {
177+ "petId": {
178+ "type": "string",
179+ "description": "Updated name of the pet"
180+ },
181+ "status": {
182+ "type": "string",
183+ "description": "Updated status of the pet"
184+ }
185+ }
186+ }
187+ }
188+ }
189+ },
190+ "responses": {
191+ "200": {
192+ "description": "Pet updated."
193+ },
194+ "405": {
195+ "description": "Invalid input"
196+ }
197+ },
198+ "security": [
199+ {
200+ "petstore_auth": [
201+ "write:pets",
202+ "read:pets"
203+ ]
204+ }
205+ ]
206+}
207+|]
208+
209+-- =======================================================================
210+-- Schema object
211+-- =======================================================================
212+
213+schemaPrimitiveExample :: Schema
214+schemaPrimitiveExample = mempty
215+ & type_ ?~ OpenApiString
216+ & format ?~ "email"
217+
218+schemaPrimitiveExampleJSON :: Value
219+schemaPrimitiveExampleJSON = [aesonQQ|
220+{
221+ "type": "string",
222+ "format": "email"
223+}
224+|]
225+
226+schemaSimpleModelExample :: Schema
227+schemaSimpleModelExample = mempty
228+ & type_ ?~ OpenApiObject
229+ & required .~ [ "name" ]
230+ & properties .~
231+ [ ("name", Inline (mempty & type_ ?~ OpenApiString))
232+ , ("address", Ref (Reference "Address"))
233+ , ("age", Inline $ mempty
234+ & minimum_ ?~ 0
235+ & type_ ?~ OpenApiInteger
236+ & format ?~ "int32" ) ]
237+
238+schemaSimpleModelExampleJSON :: Value
239+schemaSimpleModelExampleJSON = [aesonQQ|
240+{ "required": [ "name" ],
241+ "properties": {
242+ "name": {
243+ "type": "string"
244+ },
245+ "address": {
246+ "$ref": "#/components/schemas/Address"
247+ },
248+ "age": {
249+ "format": "int32",
250+ "minimum": 0,
251+ "type": "integer"
252+ }
253+ },
254+ "type": "object"
255+}
256+|]
257+
258+schemaModelDictExample :: Schema
259+schemaModelDictExample = mempty
260+ & type_ ?~ OpenApiObject
261+ & additionalProperties ?~ AdditionalPropertiesSchema (Inline (mempty & type_ ?~ OpenApiString))
262+
263+schemaModelDictExampleJSON :: Value
264+schemaModelDictExampleJSON = [aesonQQ|
265+{
266+ "type": "object",
267+ "additionalProperties": {
268+ "type": "string"
269+ }
270+}
271+|]
272+
273+schemaAdditionalExample :: Schema
274+schemaAdditionalExample = mempty
275+ & type_ ?~ OpenApiObject
276+ & additionalProperties ?~ AdditionalPropertiesAllowed True
277+
278+schemaAdditionalExampleJSON :: Value
279+schemaAdditionalExampleJSON = [aesonQQ|
280+{
281+ "type": "object",
282+ "additionalProperties": true
283+}
284+|]
285+
286+schemaWithExampleExample :: Schema
287+schemaWithExampleExample = mempty
288+ & type_ ?~ OpenApiObject
289+ & properties .~
290+ [ ("id", Inline $ mempty
291+ & type_ ?~ OpenApiInteger
292+ & format ?~ "int64" )
293+ , ("name", Inline $ mempty
294+ & type_ ?~ OpenApiString) ]
295+ & required .~ [ "name" ]
296+ & example ?~ [aesonQQ|
297+ {
298+ "name": "Puma",
299+ "id": 1
300+ }
301+ |]
302+
303+schemaWithExampleExampleJSON :: Value
304+schemaWithExampleExampleJSON = [aesonQQ|
305+{
306+ "type": "object",
307+ "properties": {
308+ "id": {
309+ "type": "integer",
310+ "format": "int64"
311+ },
312+ "name": {
313+ "type": "string"
314+ }
315+ },
316+ "required": [
317+ "name"
318+ ],
319+ "example": {
320+ "name": "Puma",
321+ "id": 1
322+ }
323+}
324+|]
325+
326+-- =======================================================================
327+-- Definitions object
328+-- =======================================================================
329+
330+definitionsExample :: HashMap Text Schema
331+definitionsExample =
332+ [ ("Category", mempty
333+ & type_ ?~ OpenApiObject
334+ & properties .~
335+ [ ("id", Inline $ mempty
336+ & type_ ?~ OpenApiInteger
337+ & format ?~ "int64")
338+ , ("name", Inline (mempty & type_ ?~ OpenApiString)) ] )
339+ , ("Tag", mempty
340+ & type_ ?~ OpenApiObject
341+ & properties .~
342+ [ ("id", Inline $ mempty
343+ & type_ ?~ OpenApiInteger
344+ & format ?~ "int64")
345+ , ("name", Inline (mempty & type_ ?~ OpenApiString)) ] ) ]
346+
347+definitionsExampleJSON :: Value
348+definitionsExampleJSON = [aesonQQ|
349+{
350+ "Category": {
351+ "type": "object",
352+ "properties": {
353+ "id": {
354+ "type": "integer",
355+ "format": "int64"
356+ },
357+ "name": {
358+ "type": "string"
359+ }
360+ }
361+ },
362+ "Tag": {
363+ "type": "object",
364+ "properties": {
365+ "id": {
366+ "type": "integer",
367+ "format": "int64"
368+ },
369+ "name": {
370+ "type": "string"
371+ }
372+ }
373+ }
374+}
375+|]
376+
377+-- =======================================================================
378+-- Parameters Definition object
379+-- =======================================================================
380+
381+paramsDefinitionExample :: HashMap Text Param
382+paramsDefinitionExample =
383+ [ ("skipParam", mempty
384+ & name .~ "skip"
385+ & description ?~ "number of items to skip"
386+ & required ?~ True
387+ & in_ .~ ParamQuery
388+ & schema ?~ Inline (mempty
389+ & type_ ?~ OpenApiInteger
390+ & format ?~ "int32" ))
391+ , ("limitParam", mempty
392+ & name .~ "limit"
393+ & description ?~ "max records to return"
394+ & required ?~ True
395+ & in_ .~ ParamQuery
396+ & schema ?~ Inline (mempty
397+ & type_ ?~ OpenApiInteger
398+ & format ?~ "int32" )) ]
399+
400+paramsDefinitionExampleJSON :: Value
401+paramsDefinitionExampleJSON = [aesonQQ|
402+{
403+ "skipParam": {
404+ "name": "skip",
405+ "in": "query",
406+ "description": "number of items to skip",
407+ "required": true,
408+ "schema": {
409+ "type": "integer",
410+ "format": "int32"
411+ }
412+ },
413+ "limitParam": {
414+ "name": "limit",
415+ "in": "query",
416+ "description": "max records to return",
417+ "required": true,
418+ "schema": {
419+ "type": "integer",
420+ "format": "int32"
421+ }
422+ }
423+}
424+|]
425+
426+-- =======================================================================
427+-- Responses Definition object
428+-- =======================================================================
429+
430+responsesDefinitionExample :: HashMap Text Response
431+responsesDefinitionExample =
432+ [ ("NotFound", mempty & description .~ "Entity not found.")
433+ , ("IllegalInput", mempty & description .~ "Illegal input for operation.") ]
434+
435+responsesDefinitionExampleJSON :: Value
436+responsesDefinitionExampleJSON = [aesonQQ|
437+{
438+ "NotFound": {
439+ "description": "Entity not found."
440+ },
441+ "IllegalInput": {
442+ "description": "Illegal input for operation."
443+ }
444+}
445+|]
446+
447+-- =======================================================================
448+-- Security Definition object
449+-- =======================================================================
450+
451+securityDefinitionsExample :: SecurityDefinitions
452+securityDefinitionsExample = SecurityDefinitions
453+ [ ("api_key", SecurityScheme
454+ { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
455+ , _securitySchemeDescription = Nothing })
456+ , ("petstore_auth", SecurityScheme
457+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
458+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
459+ , _oAath2RefreshUrl = Nothing
460+ , _oAuth2Scopes =
461+ [ ("write:pets", "modify pets in your account")
462+ , ("read:pets", "read your pets") ] } )
463+ , _securitySchemeDescription = Nothing }) ]
464+
465+securityDefinitionsExampleJSON :: Value
466+securityDefinitionsExampleJSON = [aesonQQ|
467+{
468+ "api_key": {
469+ "in": "header",
470+ "name": "api_key",
471+ "type": "apiKey"
472+ },
473+ "petstore_auth": {
474+ "type": "oauth2",
475+ "flows": {
476+ "implicit": {
477+ "scopes": {
478+ "write:pets": "modify pets in your account",
479+ "read:pets": "read your pets"
480+ },
481+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
482+ }
483+ }
484+ }
485+}
486+
487+|]
488+
489+oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
490+oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
491+ [ ("petstore_auth", SecurityScheme
492+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
493+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
494+ , _oAath2RefreshUrl = Nothing
495+ , _oAuth2Scopes =
496+ [ ("read:pets", "read your pets") ] } )
497+ , _securitySchemeDescription = Nothing })
498+ ]
499+
500+oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
501+oAuth2SecurityDefinitionsWriteExample = SecurityDefinitions
502+ [ ("petstore_auth", SecurityScheme
503+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
504+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
505+ , _oAath2RefreshUrl = Nothing
506+ , _oAuth2Scopes =
507+ [ ("write:pets", "modify pets in your account") ] } )
508+ , _securitySchemeDescription = Nothing })
509+ ]
510+
511+oAuth2SecurityDefinitionsExample :: SecurityDefinitions
512+oAuth2SecurityDefinitionsExample =
513+ oAuth2SecurityDefinitionsWriteExample <>
514+ oAuth2SecurityDefinitionsReadExample
515+
516+oAuth2SecurityDefinitionsExampleJSON :: Value
517+oAuth2SecurityDefinitionsExampleJSON = [aesonQQ|
518+{
519+ "petstore_auth": {
520+ "type": "oauth2",
521+ "flows": {
522+ "implicit": {
523+ "scopes": {
524+ "write:pets": "modify pets in your account",
525+ "read:pets": "read your pets"
526+ },
527+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
528+ }
529+ }
530+ }
531+}
532+|]
533+
534+oAuth2SecurityDefinitionsReadOpenApi :: OpenApi
535+oAuth2SecurityDefinitionsReadOpenApi =
536+ mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsReadExample
537+
538+oAuth2SecurityDefinitionsWriteOpenApi :: OpenApi
539+oAuth2SecurityDefinitionsWriteOpenApi =
540+ mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsWriteExample
541+
542+oAuth2SecurityDefinitionsOpenApi :: OpenApi
543+oAuth2SecurityDefinitionsOpenApi =
544+ mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsExample
545+
546+-- =======================================================================
547+-- Swagger object
548+-- =======================================================================
549+
550+emptyPathsFieldExample :: OpenApi
551+emptyPathsFieldExample = mempty
552+
553+emptyPathsFieldExampleJSON :: Value
554+emptyPathsFieldExampleJSON = [aesonQQ|
555+{
556+ "openapi": "3.0.0",
557+ "info": {"version": "", "title": ""},
558+ "paths": {},
559+ "components": {}
560+}
561+|]
562+
563+swaggerExample :: OpenApi
564+swaggerExample = mempty
565+ -- & basePath ?~ "/"
566+ -- & schemes ?~ [Http]
567+ & info .~ (mempty
568+ & version .~ "1.0"
569+ & title .~ "Todo API"
570+ & license ?~ "MIT"
571+ & license._Just.url ?~ URL "http://mit.com"
572+ & description ?~ "This is an API that tests servant-swagger support for a Todo API")
573+ & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
574+ & responses . at 200 ?~ Inline (mempty
575+ & description .~ "OK"
576+ & content . at "application/json" ?~ (mempty
577+ & schema ?~ Inline (mempty
578+ & type_ ?~ OpenApiObject
579+ & example ?~ [aesonQQ|
580+ {
581+ "created": 100,
582+ "description": "get milk"
583+ } |]
584+ & description ?~ "This is some real Todo right here"
585+ & properties .~
586+ [ ("created", Inline $ mempty
587+ & type_ ?~ OpenApiInteger
588+ & format ?~ "int32")
589+ , ("description", Inline (mempty & type_ ?~ OpenApiString))])))
590+ & parameters .~
591+ [ Inline $ mempty
592+ & required ?~ True
593+ & name .~ "id"
594+ & description ?~ "TodoId param"
595+ & in_ .~ ParamPath
596+ & schema ?~ Inline (mempty
597+ & type_ ?~ OpenApiString ) ]
598+ & tags .~ InsOrdHS.fromList [ "todo" ] ))
599+
600+swaggerExampleJSON :: Value
601+swaggerExampleJSON = [aesonQQ|
602+{
603+ "openapi": "3.0.0",
604+ "info": {
605+ "version": "1.0",
606+ "title": "Todo API",
607+ "license": {
608+ "url": "http://mit.com",
609+ "name": "MIT"
610+ },
611+ "description": "This is an API that tests servant-swagger support for a Todo API"
612+ },
613+ "paths": {
614+ "/todo/{id}": {
615+ "get": {
616+ "tags": [
617+ "todo"
618+ ],
619+ "parameters": [
620+ {
621+ "required": true,
622+ "schema": {
623+ "type": "string"
624+ },
625+ "in": "path",
626+ "name": "id",
627+ "description": "TodoId param"
628+ }
629+ ],
630+ "responses": {
631+ "200": {
632+ "content": {
633+ "application/json": {
634+ "schema": {
635+ "example": {
636+ "created": 100,
637+ "description": "get milk"
638+ },
639+ "type": "object",
640+ "description": "This is some real Todo right here",
641+ "properties": {
642+ "created": {
643+ "format": "int32",
644+ "type": "integer"
645+ },
646+ "description": {
647+ "type": "string"
648+ }
649+ }
650+ }
651+ }
652+ },
653+ "description": "OK"
654+ }
655+ }
656+ }
657+ }
658+ },
659+ "components": {}
660+}
661+|]
662+
663+petstoreExampleJSON :: Value
664+petstoreExampleJSON = [aesonQQ|
665+{
666+ "openapi": "3.0.0",
667+ "info": {
668+ "version": "1.0.0",
669+ "title": "Swagger Petstore",
670+ "license": {
671+ "name": "MIT"
672+ }
673+ },
674+ "servers": [
675+ {
676+ "url": "http://petstore.swagger.io/v1"
677+ }
678+ ],
679+ "paths": {
680+ "/pets": {
681+ "get": {
682+ "summary": "List all pets",
683+ "operationId": "listPets",
684+ "tags": [
685+ "pets"
686+ ],
687+ "parameters": [
688+ {
689+ "name": "limit",
690+ "in": "query",
691+ "description": "How many items to return at one time (max 100)",
692+ "required": false,
693+ "schema": {
694+ "type": "integer",
695+ "format": "int32"
696+ }
697+ }
698+ ],
699+ "responses": {
700+ "200": {
701+ "description": "A paged array of pets",
702+ "headers": {
703+ "x-next": {
704+ "description": "A link to the next page of responses",
705+ "schema": {
706+ "type": "string"
707+ }
708+ }
709+ },
710+ "content": {
711+ "application/json": {
712+ "schema": {
713+ "type": "array",
714+ "items": {
715+ "type": "object",
716+ "required": [
717+ "id",
718+ "name"
719+ ],
720+ "properties": {
721+ "id": {
722+ "type": "integer",
723+ "format": "int64"
724+ },
725+ "name": {
726+ "type": "string"
727+ },
728+ "tag": {
729+ "type": "string"
730+ }
731+ }
732+ }
733+ }
734+ }
735+ }
736+ },
737+ "default": {
738+ "description": "unexpected error",
739+ "content": {
740+ "application/json": {
741+ "schema": {
742+ "type": "object",
743+ "required": [
744+ "code",
745+ "message"
746+ ],
747+ "properties": {
748+ "code": {
749+ "type": "integer",
750+ "format": "int32"
751+ },
752+ "message": {
753+ "type": "string"
754+ }
755+ }
756+ }
757+ }
758+ }
759+ }
760+ }
761+ },
762+ "post": {
763+ "summary": "Create a pet",
764+ "operationId": "createPets",
765+ "tags": [
766+ "pets"
767+ ],
768+ "responses": {
769+ "201": {
770+ "description": "Null response"
771+ },
772+ "default": {
773+ "description": "unexpected error",
774+ "content": {
775+ "application/json": {
776+ "schema": {
777+ "type": "object",
778+ "required": [
779+ "code",
780+ "message"
781+ ],
782+ "properties": {
783+ "code": {
784+ "type": "integer",
785+ "format": "int32"
786+ },
787+ "message": {
788+ "type": "string"
789+ }
790+ }
791+ }
792+ }
793+ }
794+ }
795+ }
796+ }
797+ },
798+ "/pets/{petId}": {
799+ "get": {
800+ "summary": "Info for a specific pet",
801+ "operationId": "showPetById",
802+ "tags": [
803+ "pets"
804+ ],
805+ "parameters": [
806+ {
807+ "name": "petId",
808+ "in": "path",
809+ "required": true,
810+ "description": "The id of the pet to retrieve",
811+ "schema": {
812+ "type": "string"
813+ }
814+ }
815+ ],
816+ "responses": {
817+ "200": {
818+ "description": "Expected response to a valid request",
819+ "content": {
820+ "application/json": {
821+ "schema": {
822+ "type": "object",
823+ "required": [
824+ "id",
825+ "name"
826+ ],
827+ "properties": {
828+ "id": {
829+ "type": "integer",
830+ "format": "int64"
831+ },
832+ "name": {
833+ "type": "string"
834+ },
835+ "tag": {
836+ "type": "string"
837+ }
838+ }
839+ }
840+ }
841+ }
842+ },
843+ "default": {
844+ "description": "unexpected error",
845+ "content": {
846+ "application/json": {
847+ "schema": {
848+ "type": "object",
849+ "required": [
850+ "code",
851+ "message"
852+ ],
853+ "properties": {
854+ "code": {
855+ "type": "integer",
856+ "format": "int32"
857+ },
858+ "message": {
859+ "type": "string"
860+ }
861+ }
862+ }
863+ }
864+ }
865+ }
866+ }
867+ }
868+ }
869+ },
870+ "components": {
871+ "schemas": {
872+ "Pet": {
873+ "type": "object",
874+ "required": [
875+ "id",
876+ "name"
877+ ],
878+ "properties": {
879+ "id": {
880+ "type": "integer",
881+ "format": "int64"
882+ },
883+ "name": {
884+ "type": "string"
885+ },
886+ "tag": {
887+ "type": "string"
888+ }
889+ }
890+ },
891+ "Pets": {
892+ "type": "array",
893+ "items": {
894+ "type": "object",
895+ "required": [
896+ "id",
897+ "name"
898+ ],
899+ "properties": {
900+ "id": {
901+ "type": "integer",
902+ "format": "int64"
903+ },
904+ "name": {
905+ "type": "string"
906+ },
907+ "tag": {
908+ "type": "string"
909+ }
910+ }
911+ }
912+ },
913+ "Error": {
914+ "type": "object",
915+ "required": [
916+ "code",
917+ "message"
918+ ],
919+ "properties": {
920+ "code": {
921+ "type": "integer",
922+ "format": "int32"
923+ },
924+ "message": {
925+ "type": "string"
926+ }
927+ }
928+ }
929+ }
930+ }
931+}
932+|]
933+
934+compositionSchemaExample :: Schema
935+compositionSchemaExample = mempty
936+ & type_ ?~ OpenApiObject
937+ & Data.OpenApi.allOf ?~ [
938+ Ref (Reference "Other")
939+ , Inline (mempty
940+ & type_ ?~ OpenApiObject
941+ & properties .~
942+ [ ("greet", Inline $ mempty
943+ & type_ ?~ OpenApiString) ])
944+ ]
945+
946+compositionSchemaExampleJSON :: Value
947+compositionSchemaExampleJSON = [aesonQQ|
948+{
949+ "type": "object",
950+ "allOf": [
951+ {
952+ "$ref": "#/components/schemas/Other"
953+ },
954+ {
955+ "type": "object",
956+ "properties": {
957+ "greet": { "type": "string" }
958+ }
959+ }
960+ ]
961+}
962+|]
test/Data/SwaggerSpec.hsdeleted−1715
@@ -1,1715 +0,0 @@
1−{-# LANGUAGE OverloadedLists #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE QuasiQuotes #-}
5−module Data.SwaggerSpec where
6−
7−import Prelude ()
8−import Prelude.Compat
9−
10−import Control.Lens
11−
12−import Data.Aeson
13−import Data.Aeson.QQ.Simple
14−import Data.HashMap.Strict (HashMap)
15−import qualified Data.HashSet.InsOrd as InsOrdHS
16−import Data.Text (Text)
17−
18−import Data.Swagger
19−import SpecCommon
20−import Test.Hspec hiding (example)
21−
22−spec :: Spec
23−spec = do
24− describe "host" $ do
25− it "can decode the host port" $ do
26− let h = Just $ Host "localhost" (Just (fromInteger 8000))
27− swagger :: Swagger
28− swagger = swaggerExample
29− & host .~ h
30− parsed :: Swagger = either error id $ eitherDecode' $ encode swagger
31− parsed ^. host `shouldBe` h
32− describe "License Object" $ licenseExample <=> licenseExampleJSON
33− describe "Contact Object" $ contactExample <=> contactExampleJSON
34− describe "Info Object" $ infoExample <=> infoExampleJSON
35− describe "Operation Object" $ operationExample <=> operationExampleJSON
36− describe "Schema Object" $ do
37− context "Primitive Sample" $ schemaPrimitiveExample <=> schemaPrimitiveExampleJSON
38− context "Simple Model" $ schemaSimpleModelExample <=> schemaSimpleModelExampleJSON
39− context "Model with Map/Dictionary Properties" $ schemaModelDictExample <=> schemaModelDictExampleJSON
40− context "Model with Arbitrary Properties" $ schemaAdditionalExample <=> schemaAdditionalExampleJSON
41− context "Model with Example" $ schemaWithExampleExample <=> schemaWithExampleExampleJSON
42− describe "Definitions Object" $ definitionsExample <=> definitionsExampleJSON
43− describe "Parameters Definition Object" $ paramsDefinitionExample <=> paramsDefinitionExampleJSON
44− describe "Responses Definition Object" $ responsesDefinitionExample <=> responsesDefinitionExampleJSON
45− describe "Security Definitions Object" $ securityDefinitionsExample <=> securityDefinitionsExampleJSON
46− describe "OAuth2 Security Definitions with merged Scope" $ oAuth2SecurityDefinitionsExample <=> oAuth2SecurityDefinitionsExampleJSON
47− describe "Composition Schema Example" $ compositionSchemaExample <=> compositionSchemaExampleJSON
48− describe "Swagger Object" $ do
49− context "Example with no paths" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
50− context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
51− context "PetStore Example" $
52− it "decodes successfully" $ do
53− fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: Swagger) -> True; _ -> False)
54−
55−main :: IO ()
56−main = hspec spec
57−
58−-- =======================================================================
59−-- Info object
60−-- =======================================================================
61−
62−infoExample :: Info
63−infoExample = mempty
64− & title .~ "Swagger Sample App"
65− & description ?~ "This is a sample server Petstore server."
66− & termsOfService ?~ "http://swagger.io/terms/"
67− & contact ?~ contactExample
68− & license ?~ licenseExample
69− & version .~ "1.0.1"
70−
71−infoExampleJSON :: Value
72−infoExampleJSON = [aesonQQ|
73−{
74− "title": "Swagger Sample App",
75− "description": "This is a sample server Petstore server.",
76− "termsOfService": "http://swagger.io/terms/",
77− "contact": {
78− "name": "API Support",
79− "url": "http://www.swagger.io/support",
80− "email": "support@swagger.io"
81− },
82− "license": {
83− "name": "Apache 2.0",
84− "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
85− },
86− "version": "1.0.1"
87−}
88−|]
89−
90−-- =======================================================================
91−-- Contact object
92−-- =======================================================================
93−
94−contactExample :: Contact
95−contactExample = mempty
96− & name ?~ "API Support"
97− & url ?~ URL "http://www.swagger.io/support"
98− & email ?~ "support@swagger.io"
99−
100−contactExampleJSON :: Value
101−contactExampleJSON = [aesonQQ|
102−{
103− "name": "API Support",
104− "url": "http://www.swagger.io/support",
105− "email": "support@swagger.io"
106−}
107−|]
108−
109−-- =======================================================================
110−-- License object
111−-- =======================================================================
112−
113−licenseExample :: License
114−licenseExample = "Apache 2.0"
115− & url ?~ URL "http://www.apache.org/licenses/LICENSE-2.0.html"
116−
117−licenseExampleJSON :: Value
118−licenseExampleJSON = [aesonQQ|
119−{
120− "name": "Apache 2.0",
121− "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
122−}
123−|]
124−
125−
126−-- =======================================================================
127−-- Operation object
128−-- =======================================================================
129−
130−operationExample :: Operation
131−operationExample = mempty
132− & tags .~ InsOrdHS.fromList ["pet"]
133− & summary ?~ "Updates a pet in the store with form data"
134− & description ?~ ""
135− & operationId ?~ "updatePetWithForm"
136− & consumes ?~ MimeList ["application/x-www-form-urlencoded"]
137− & produces ?~ MimeList ["application/json", "application/xml"]
138− & parameters .~ map Inline
139− [ mempty
140− & name .~ "petId"
141− & description ?~ "ID of pet that needs to be updated"
142− & required ?~ True
143− & schema .~ ParamOther (stringSchema ParamPath)
144− , mempty
145− & name .~ "name"
146− & description ?~ "Updated name of the pet"
147− & required ?~ False
148− & schema .~ ParamOther (stringSchema ParamFormData)
149− , mempty
150− & name .~ "status"
151− & description ?~ "Updated status of the pet"
152− & required ?~ False
153− & schema .~ ParamOther (stringSchema ParamFormData)
154− ]
155−
156− & at 200 ?~ "Pet updated."
157− & at 405 ?~ "Invalid input"
158− & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
159− where
160− stringSchema :: ParamLocation -> ParamOtherSchema
161− stringSchema loc = mempty
162− & in_ .~ loc
163− & type_ ?~ SwaggerString
164−
165−operationExampleJSON :: Value
166−operationExampleJSON = [aesonQQ|
167−{
168− "tags": [
169− "pet"
170− ],
171− "summary": "Updates a pet in the store with form data",
172− "description": "",
173− "operationId": "updatePetWithForm",
174− "consumes": [
175− "application/x-www-form-urlencoded"
176− ],
177− "produces": [
178− "application/json",
179− "application/xml"
180− ],
181− "parameters": [
182− {
183− "name": "petId",
184− "in": "path",
185− "description": "ID of pet that needs to be updated",
186− "required": true,
187− "type": "string"
188− },
189− {
190− "name": "name",
191− "in": "formData",
192− "description": "Updated name of the pet",
193− "required": false,
194− "type": "string"
195− },
196− {
197− "name": "status",
198− "in": "formData",
199− "description": "Updated status of the pet",
200− "required": false,
201− "type": "string"
202− }
203− ],
204− "responses": {
205− "200": {
206− "description": "Pet updated."
207− },
208− "405": {
209− "description": "Invalid input"
210− }
211− },
212− "security": [
213− {
214− "petstore_auth": [
215− "write:pets",
216− "read:pets"
217− ]
218− }
219− ]
220−}
221−|]
222−
223−-- =======================================================================
224−-- Schema object
225−-- =======================================================================
226−
227−schemaPrimitiveExample :: Schema
228−schemaPrimitiveExample = mempty
229− & type_ ?~ SwaggerString
230− & format ?~ "email"
231−
232−schemaPrimitiveExampleJSON :: Value
233−schemaPrimitiveExampleJSON = [aesonQQ|
234−{
235− "type": "string",
236− "format": "email"
237−}
238−|]
239−
240−schemaSimpleModelExample :: Schema
241−schemaSimpleModelExample = mempty
242− & type_ ?~ SwaggerObject
243− & required .~ [ "name" ]
244− & properties .~
245− [ ("name", Inline (mempty & type_ ?~ SwaggerString))
246− , ("address", Ref (Reference "Address"))
247− , ("age", Inline $ mempty
248− & minimum_ ?~ 0
249− & type_ ?~ SwaggerInteger
250− & format ?~ "int32" ) ]
251−
252−schemaSimpleModelExampleJSON :: Value
253−schemaSimpleModelExampleJSON = [aesonQQ|
254−{
255− "type": "object",
256− "required": [
257− "name"
258− ],
259− "properties": {
260− "name": {
261− "type": "string"
262− },
263− "address": {
264− "$ref": "#/definitions/Address"
265− },
266− "age": {
267− "type": "integer",
268− "format": "int32",
269− "minimum": 0
270− }
271− }
272−}
273−|]
274−
275−schemaModelDictExample :: Schema
276−schemaModelDictExample = mempty
277− & type_ ?~ SwaggerObject
278− & additionalProperties ?~ AdditionalPropertiesSchema (Inline (mempty & type_ ?~ SwaggerString))
279−
280−schemaModelDictExampleJSON :: Value
281−schemaModelDictExampleJSON = [aesonQQ|
282−{
283− "type": "object",
284− "additionalProperties": {
285− "type": "string"
286− }
287−}
288−|]
289−
290−schemaAdditionalExample :: Schema
291−schemaAdditionalExample = mempty
292− & type_ ?~ SwaggerObject
293− & additionalProperties ?~ AdditionalPropertiesAllowed True
294−
295−schemaAdditionalExampleJSON :: Value
296−schemaAdditionalExampleJSON = [aesonQQ|
297−{
298− "type": "object",
299− "additionalProperties": true
300−}
301−|]
302−
303−schemaWithExampleExample :: Schema
304−schemaWithExampleExample = mempty
305− & type_ ?~ SwaggerObject
306− & properties .~
307− [ ("id", Inline $ mempty
308− & type_ ?~ SwaggerInteger
309− & format ?~ "int64" )
310− , ("name", Inline $ mempty
311− & type_ ?~ SwaggerString) ]
312− & required .~ [ "name" ]
313− & example ?~ [aesonQQ|
314− {
315− "name": "Puma",
316− "id": 1
317− }
318− |]
319−
320−schemaWithExampleExampleJSON :: Value
321−schemaWithExampleExampleJSON = [aesonQQ|
322−{
323− "type": "object",
324− "properties": {
325− "id": {
326− "type": "integer",
327− "format": "int64"
328− },
329− "name": {
330− "type": "string"
331− }
332− },
333− "required": [
334− "name"
335− ],
336− "example": {
337− "name": "Puma",
338− "id": 1
339− }
340−}
341−|]
342−
343−-- =======================================================================
344−-- Definitions object
345−-- =======================================================================
346−
347−definitionsExample :: HashMap Text Schema
348−definitionsExample =
349− [ ("Category", mempty
350− & type_ ?~ SwaggerObject
351− & properties .~
352− [ ("id", Inline $ mempty
353− & type_ ?~ SwaggerInteger
354− & format ?~ "int64")
355− , ("name", Inline (mempty & type_ ?~ SwaggerString)) ] )
356− , ("Tag", mempty
357− & type_ ?~ SwaggerObject
358− & properties .~
359− [ ("id", Inline $ mempty
360− & type_ ?~ SwaggerInteger
361− & format ?~ "int64")
362− , ("name", Inline (mempty & type_ ?~ SwaggerString)) ] ) ]
363−
364−definitionsExampleJSON :: Value
365−definitionsExampleJSON = [aesonQQ|
366−{
367− "Category": {
368− "type": "object",
369− "properties": {
370− "id": {
371− "type": "integer",
372− "format": "int64"
373− },
374− "name": {
375− "type": "string"
376− }
377− }
378− },
379− "Tag": {
380− "type": "object",
381− "properties": {
382− "id": {
383− "type": "integer",
384− "format": "int64"
385− },
386− "name": {
387− "type": "string"
388− }
389− }
390− }
391−}
392−|]
393−
394−-- =======================================================================
395−-- Parameters Definition object
396−-- =======================================================================
397−
398−paramsDefinitionExample :: HashMap Text Param
399−paramsDefinitionExample =
400− [ ("skipParam", mempty
401− & name .~ "skip"
402− & description ?~ "number of items to skip"
403− & required ?~ True
404− & schema .~ ParamOther (mempty
405− & in_ .~ ParamQuery
406− & type_ ?~ SwaggerInteger
407− & format ?~ "int32" ))
408− , ("limitParam", mempty
409− & name .~ "limit"
410− & description ?~ "max records to return"
411− & required ?~ True
412− & schema .~ ParamOther (mempty
413− & in_ .~ ParamQuery
414− & type_ ?~ SwaggerInteger
415− & format ?~ "int32" )) ]
416−
417−paramsDefinitionExampleJSON :: Value
418−paramsDefinitionExampleJSON = [aesonQQ|
419−{
420− "skipParam": {
421− "name": "skip",
422− "in": "query",
423− "description": "number of items to skip",
424− "required": true,
425− "type": "integer",
426− "format": "int32"
427− },
428− "limitParam": {
429− "name": "limit",
430− "in": "query",
431− "description": "max records to return",
432− "required": true,
433− "type": "integer",
434− "format": "int32"
435− }
436−}
437−|]
438−
439−-- =======================================================================
440−-- Responses Definition object
441−-- =======================================================================
442−
443−responsesDefinitionExample :: HashMap Text Response
444−responsesDefinitionExample =
445− [ ("NotFound", mempty & description .~ "Entity not found.")
446− , ("IllegalInput", mempty & description .~ "Illegal input for operation.") ]
447−
448−responsesDefinitionExampleJSON :: Value
449−responsesDefinitionExampleJSON = [aesonQQ|
450−{
451− "NotFound": {
452− "description": "Entity not found."
453− },
454− "IllegalInput": {
455− "description": "Illegal input for operation."
456− }
457−}
458−|]
459−
460−-- =======================================================================
461−-- Responses Definition object
462−-- =======================================================================
463−
464−securityDefinitionsExample :: SecurityDefinitions
465−securityDefinitionsExample = SecurityDefinitions
466− [ ("api_key", SecurityScheme
467− { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
468− , _securitySchemeDescription = Nothing })
469− , ("petstore_auth", SecurityScheme
470− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
471− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
472− , _oauth2Scopes =
473− [ ("write:pets", "modify pets in your account")
474− , ("read:pets", "read your pets") ] } )
475− , _securitySchemeDescription = Nothing }) ]
476−
477−securityDefinitionsExampleJSON :: Value
478−securityDefinitionsExampleJSON = [aesonQQ|
479−{
480− "api_key": {
481− "type": "apiKey",
482− "name": "api_key",
483− "in": "header"
484− },
485− "petstore_auth": {
486− "type": "oauth2",
487− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
488− "flow": "implicit",
489− "scopes": {
490− "write:pets": "modify pets in your account",
491− "read:pets": "read your pets"
492− }
493− }
494−}
495−|]
496−
497−oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
498−oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
499− [ ("petstore_auth", SecurityScheme
500− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
501− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
502− , _oauth2Scopes =
503− [ ("read:pets", "read your pets") ] } )
504− , _securitySchemeDescription = Nothing })
505− ]
506−
507−oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
508−oAuth2SecurityDefinitionsWriteExample = SecurityDefinitions
509− [ ("petstore_auth", SecurityScheme
510− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
511− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
512− , _oauth2Scopes =
513− [ ("write:pets", "modify pets in your account") ] } )
514− , _securitySchemeDescription = Nothing })
515− ]
516−
517−oAuth2SecurityDefinitionsExample :: SecurityDefinitions
518−oAuth2SecurityDefinitionsExample =
519− oAuth2SecurityDefinitionsWriteExample <>
520− oAuth2SecurityDefinitionsReadExample
521−
522−oAuth2SecurityDefinitionsExampleJSON :: Value
523−oAuth2SecurityDefinitionsExampleJSON = [aesonQQ|
524−{
525− "petstore_auth": {
526− "type": "oauth2",
527− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
528− "flow": "implicit",
529− "scopes": {
530− "write:pets": "modify pets in your account",
531− "read:pets": "read your pets"
532− }
533− }
534−}
535−|]
536−
537−-- =======================================================================
538−-- Swagger object
539−-- =======================================================================
540−
541−emptyPathsFieldExample :: Swagger
542−emptyPathsFieldExample = mempty
543−
544−emptyPathsFieldExampleJSON :: Value
545−emptyPathsFieldExampleJSON = [aesonQQ|
546−{
547− "swagger": "2.0",
548− "info": {"version": "", "title": ""},
549− "paths": {}
550−}
551−|]
552−
553−swaggerExample :: Swagger
554−swaggerExample = mempty
555− & basePath ?~ "/"
556− & schemes ?~ [Http]
557− & info .~ (mempty
558− & version .~ "1.0"
559− & title .~ "Todo API"
560− & license ?~ "MIT"
561− & license._Just.url ?~ URL "http://mit.com"
562− & description ?~ "This is a an API that tests servant-swagger support for a Todo API")
563− & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
564− & at 200 ?~ Inline (mempty
565− & description .~ "OK"
566− & schema ?~ Inline (mempty
567− & type_ ?~ SwaggerObject
568− & example ?~ [aesonQQ|
569− {
570− "created": 100,
571− "description": "get milk"
572− } |]
573− & description ?~ "This is some real Todo right here"
574− & properties .~
575− [ ("created", Inline $ mempty
576− & type_ ?~ SwaggerInteger
577− & format ?~ "int32")
578− , ("description", Inline (mempty & type_ ?~ SwaggerString))]))
579− & produces ?~ MimeList [ "application/json" ]
580− & parameters .~
581− [ Inline $ mempty
582− & required ?~ True
583− & name .~ "id"
584− & description ?~ "TodoId param"
585− & schema .~ ParamOther (mempty
586− & in_ .~ ParamPath
587− & type_ ?~ SwaggerString ) ]
588− & tags .~ InsOrdHS.fromList [ "todo" ] ))
589−
590−swaggerExampleJSON :: Value
591−swaggerExampleJSON = [aesonQQ|
592−{
593− "swagger": "2.0",
594− "basePath": "/",
595− "schemes": [
596− "http"
597− ],
598− "info": {
599− "version": "1.0",
600− "title": "Todo API",
601− "license": {
602− "url": "http://mit.com",
603− "name": "MIT"
604− },
605− "description": "This is a an API that tests servant-swagger support for a Todo API"
606− },
607− "paths": {
608− "/todo/{id}": {
609− "get": {
610− "responses": {
611− "200": {
612− "schema": {
613− "example": {
614− "created": 100,
615− "description": "get milk"
616− },
617− "type": "object",
618− "description": "This is some real Todo right here",
619− "properties": {
620− "created": {
621− "format": "int32",
622− "type": "integer"
623− },
624− "description": {
625− "type": "string"
626− }
627− }
628− },
629− "description": "OK"
630− }
631− },
632− "produces": [
633− "application/json"
634− ],
635− "parameters": [
636− {
637− "required": true,
638− "in": "path",
639− "name": "id",
640− "type": "string",
641− "description": "TodoId param"
642− }
643− ],
644− "tags": [
645− "todo"
646− ]
647− }
648− }
649− }
650−}
651−|]
652−
653−petstoreExampleJSON :: Value
654−petstoreExampleJSON = [aesonQQ|
655−{
656− "swagger":"2.0",
657− "info":{
658− "description":"This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
659− "version":"1.0.0",
660− "title":"Swagger Petstore",
661− "termsOfService":"http://swagger.io/terms/",
662− "contact":{
663− "email":"apiteam@swagger.io"
664− },
665− "license":{
666− "name":"Apache 2.0",
667− "url":"http://www.apache.org/licenses/LICENSE-2.0.html"
668− }
669− },
670− "host":"petstore.swagger.io",
671− "basePath":"/v2",
672− "tags":[
673− {
674− "name":"pet",
675− "description":"Everything about your Pets",
676− "externalDocs":{
677− "description":"Find out more",
678− "url":"http://swagger.io"
679− }
680− },
681− {
682− "name":"store",
683− "description":"Access to Petstore orders"
684− },
685− {
686− "name":"user",
687− "description":"Operations about user",
688− "externalDocs":{
689− "description":"Find out more about our store",
690− "url":"http://swagger.io"
691− }
692− }
693− ],
694− "schemes":[
695− "http"
696− ],
697− "paths":{
698− "/pet":{
699− "post":{
700− "tags":[
701− "pet"
702− ],
703− "summary":"Add a new pet to the store",
704− "description":"",
705− "operationId":"addPet",
706− "consumes":[
707− "application/json",
708− "application/xml"
709− ],
710− "produces":[
711− "application/xml",
712− "application/json"
713− ],
714− "parameters":[
715− {
716− "in":"body",
717− "name":"body",
718− "description":"Pet object that needs to be added to the store",
719− "required":true,
720− "schema":{
721− "$ref":"#/definitions/Pet"
722− }
723− }
724− ],
725− "responses":{
726− "405":{
727− "description":"Invalid input"
728− }
729− },
730− "security":[
731− {
732− "petstore_auth":[
733− "write:pets",
734− "read:pets"
735− ]
736− }
737− ]
738− },
739− "put":{
740− "tags":[
741− "pet"
742− ],
743− "summary":"Update an existing pet",
744− "description":"",
745− "operationId":"updatePet",
746− "consumes":[
747− "application/json",
748− "application/xml"
749− ],
750− "produces":[
751− "application/xml",
752− "application/json"
753− ],
754− "parameters":[
755− {
756− "in":"body",
757− "name":"body",
758− "description":"Pet object that needs to be added to the store",
759− "required":true,
760− "schema":{
761− "$ref":"#/definitions/Pet"
762− }
763− }
764− ],
765− "responses":{
766− "400":{
767− "description":"Invalid ID supplied"
768− },
769− "404":{
770− "description":"Pet not found"
771− },
772− "405":{
773− "description":"Validation exception"
774− }
775− },
776− "security":[
777− {
778− "petstore_auth":[
779− "write:pets",
780− "read:pets"
781− ]
782− }
783− ]
784− }
785− },
786− "/pet/findByStatus":{
787− "get":{
788− "tags":[
789− "pet"
790− ],
791− "summary":"Finds Pets by status",
792− "description":"Multiple status values can be provided with comma seperated strings",
793− "operationId":"findPetsByStatus",
794− "produces":[
795− "application/xml",
796− "application/json"
797− ],
798− "parameters":[
799− {
800− "name":"status",
801− "in":"query",
802− "description":"Status values that need to be considered for filter",
803− "required":true,
804− "type":"array",
805− "items":{
806− "type":"string",
807− "enum":[
808− "available",
809− "pending",
810− "sold"
811− ],
812− "default":"available"
813− },
814− "collectionFormat":"csv"
815− }
816− ],
817− "responses":{
818− "200":{
819− "description":"successful operation",
820− "schema":{
821− "type":"array",
822− "items":{
823− "$ref":"#/definitions/Pet"
824− }
825− }
826− },
827− "400":{
828− "description":"Invalid status value"
829− }
830− },
831− "security":[
832− {
833− "petstore_auth":[
834− "write:pets",
835− "read:pets"
836− ]
837− }
838− ]
839− }
840− },
841− "/pet/findByTags":{
842− "get":{
843− "tags":[
844− "pet"
845− ],
846− "summary":"Finds Pets by tags",
847− "description":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.",
848− "operationId":"findPetsByTags",
849− "produces":[
850− "application/xml",
851− "application/json"
852− ],
853− "parameters":[
854− {
855− "name":"tags",
856− "in":"query",
857− "description":"Tags to filter by",
858− "required":true,
859− "type":"array",
860− "items":{
861− "type":"string"
862− },
863− "collectionFormat":"csv"
864− }
865− ],
866− "responses":{
867− "200":{
868− "description":"successful operation",
869− "schema":{
870− "type":"array",
871− "items":{
872− "$ref":"#/definitions/Pet"
873− }
874− }
875− },
876− "400":{
877− "description":"Invalid tag value"
878− }
879− },
880− "security":[
881− {
882− "petstore_auth":[
883− "write:pets",
884− "read:pets"
885− ]
886− }
887− ]
888− }
889− },
890− "/pet/{petId}":{
891− "get":{
892− "tags":[
893− "pet"
894− ],
895− "summary":"Find pet by ID",
896− "description":"Returns a single pet",
897− "operationId":"getPetById",
898− "produces":[
899− "application/xml",
900− "application/json"
901− ],
902− "parameters":[
903− {
904− "name":"petId",
905− "in":"path",
906− "description":"ID of pet to return",
907− "required":true,
908− "type":"integer",
909− "format":"int64"
910− }
911− ],
912− "responses":{
913− "200":{
914− "description":"successful operation",
915− "schema":{
916− "$ref":"#/definitions/Pet"
917− }
918− },
919− "400":{
920− "description":"Invalid ID supplied"
921− },
922− "404":{
923− "description":"Pet not found"
924− }
925− },
926− "security":[
927− {
928− "api_key": []
929− }
930− ]
931− },
932− "post":{
933− "tags":[
934− "pet"
935− ],
936− "summary":"Updates a pet in the store with form data",
937− "description":"",
938− "operationId":"updatePetWithForm",
939− "consumes":[
940− "application/x-www-form-urlencoded"
941− ],
942− "produces":[
943− "application/xml",
944− "application/json"
945− ],
946− "parameters":[
947− {
948− "name":"petId",
949− "in":"path",
950− "description":"ID of pet that needs to be updated",
951− "required":true,
952− "type":"integer",
953− "format":"int64"
954− },
955− {
956− "name":"name",
957− "in":"formData",
958− "description":"Updated name of the pet",
959− "required":false,
960− "type":"string"
961− },
962− {
963− "name":"status",
964− "in":"formData",
965− "description":"Updated status of the pet",
966− "required":false,
967− "type":"string"
968− }
969− ],
970− "responses":{
971− "405":{
972− "description":"Invalid input"
973− }
974− },
975− "security":[
976− {
977− "petstore_auth":[
978− "write:pets",
979− "read:pets"
980− ]
981− }
982− ]
983− },
984− "delete":{
985− "tags":[
986− "pet"
987− ],
988− "summary":"Deletes a pet",
989− "description":"",
990− "operationId":"deletePet",
991− "produces":[
992− "application/xml",
993− "application/json"
994− ],
995− "parameters":[
996− {
997− "name":"api_key",
998− "in":"header",
999− "required":false,
1000− "type":"string"
1001− },
1002− {
1003− "name":"petId",
1004− "in":"path",
1005− "description":"Pet id to delete",
1006− "required":true,
1007− "type":"integer",
1008− "format":"int64"
1009− }
1010− ],
1011− "responses":{
1012− "400":{
1013− "description":"Invalid pet value"
1014− }
1015− },
1016− "security":[
1017− {
1018− "petstore_auth":[
1019− "write:pets",
1020− "read:pets"
1021− ]
1022− }
1023− ]
1024− }
1025− },
1026− "/pet/{petId}/uploadImage":{
1027− "post":{
1028− "tags":[
1029− "pet"
1030− ],
1031− "summary":"uploads an image",
1032− "description":"",
1033− "operationId":"uploadFile",
1034− "consumes":[
1035− "multipart/form-data"
1036− ],
1037− "produces":[
1038− "application/json"
1039− ],
1040− "parameters":[
1041− {
1042− "name":"petId",
1043− "in":"path",
1044− "description":"ID of pet to update",
1045− "required":true,
1046− "type":"integer",
1047− "format":"int64"
1048− },
1049− {
1050− "name":"additionalMetadata",
1051− "in":"formData",
1052− "description":"Additional data to pass to server",
1053− "required":false,
1054− "type":"string"
1055− },
1056− {
1057− "name":"file",
1058− "in":"formData",
1059− "description":"file to upload",
1060− "required":false,
1061− "type":"file"
1062− }
1063− ],
1064− "responses":{
1065− "200":{
1066− "description":"successful operation",
1067− "schema":{
1068− "$ref":"#/definitions/ApiResponse"
1069− }
1070− }
1071− },
1072− "security":[
1073− {
1074− "petstore_auth":[
1075− "write:pets",
1076− "read:pets"
1077− ]
1078− }
1079− ]
1080− }
1081− },
1082− "/store/inventory":{
1083− "get":{
1084− "tags":[
1085− "store"
1086− ],
1087− "summary":"Returns pet inventories by status",
1088− "description":"Returns a map of status codes to quantities",
1089− "operationId":"getInventory",
1090− "produces":[
1091− "application/json"
1092− ],
1093− "parameters": [],
1094− "responses":{
1095− "200":{
1096− "description":"successful operation",
1097− "schema":{
1098− "type":"object",
1099− "additionalProperties":{
1100− "type":"integer",
1101− "format":"int32"
1102− }
1103− }
1104− }
1105− },
1106− "security":[
1107− {
1108− "api_key": []
1109− }
1110− ]
1111− }
1112− },
1113− "/store/order":{
1114− "post":{
1115− "tags":[
1116− "store"
1117− ],
1118− "summary":"Place an order for a pet",
1119− "description":"",
1120− "operationId":"placeOrder",
1121− "produces":[
1122− "application/xml",
1123− "application/json"
1124− ],
1125− "parameters":[
1126− {
1127− "in":"body",
1128− "name":"body",
1129− "description":"order placed for purchasing the pet",
1130− "required":true,
1131− "schema":{
1132− "$ref":"#/definitions/Order"
1133− }
1134− }
1135− ],
1136− "responses":{
1137− "200":{
1138− "description":"successful operation",
1139− "schema":{
1140− "$ref":"#/definitions/Order"
1141− }
1142− },
1143− "400":{
1144− "description":"Invalid Order"
1145− }
1146− }
1147− }
1148− },
1149− "/store/order/{orderId}":{
1150− "get":{
1151− "tags":[
1152− "store"
1153− ],
1154− "summary":"Find purchase order by ID",
1155− "description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",
1156− "operationId":"getOrderById",
1157− "produces":[
1158− "application/xml",
1159− "application/json"
1160− ],
1161− "parameters":[
1162− {
1163− "name":"orderId",
1164− "in":"path",
1165− "description":"ID of pet that needs to be fetched",
1166− "required":true,
1167− "type":"integer",
1168− "maximum":5.0,
1169− "minimum":1.0,
1170− "format":"int64"
1171− }
1172− ],
1173− "responses":{
1174− "200":{
1175− "description":"successful operation",
1176− "schema":{
1177− "$ref":"#/definitions/Order"
1178− }
1179− },
1180− "400":{
1181− "description":"Invalid ID supplied"
1182− },
1183− "404":{
1184− "description":"Order not found"
1185− }
1186− }
1187− },
1188− "delete":{
1189− "tags":[
1190− "store"
1191− ],
1192− "summary":"Delete purchase order by ID",
1193− "description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
1194− "operationId":"deleteOrder",
1195− "produces":[
1196− "application/xml",
1197− "application/json"
1198− ],
1199− "parameters":[
1200− {
1201− "name":"orderId",
1202− "in":"path",
1203− "description":"ID of the order that needs to be deleted",
1204− "required":true,
1205− "type":"string",
1206− "minimum":1.0
1207− }
1208− ],
1209− "responses":{
1210− "400":{
1211− "description":"Invalid ID supplied"
1212− },
1213− "404":{
1214− "description":"Order not found"
1215− }
1216− }
1217− }
1218− },
1219− "/user":{
1220− "post":{
1221− "tags":[
1222− "user"
1223− ],
1224− "summary":"Create user",
1225− "description":"This can only be done by the logged in user.",
1226− "operationId":"createUser",
1227− "produces":[
1228− "application/xml",
1229− "application/json"
1230− ],
1231− "parameters":[
1232− {
1233− "in":"body",
1234− "name":"body",
1235− "description":"Created user object",
1236− "required":true,
1237− "schema":{
1238− "$ref":"#/definitions/User"
1239− }
1240− }
1241− ],
1242− "responses":{
1243− "default":{
1244− "description":"successful operation"
1245− }
1246− }
1247− }
1248− },
1249− "/user/createWithArray":{
1250− "post":{
1251− "tags":[
1252− "user"
1253− ],
1254− "summary":"Creates list of users with given input array",
1255− "description":"",
1256− "operationId":"createUsersWithArrayInput",
1257− "produces":[
1258− "application/xml",
1259− "application/json"
1260− ],
1261− "parameters":[
1262− {
1263− "in":"body",
1264− "name":"body",
1265− "description":"List of user object",
1266− "required":true,
1267− "schema":{
1268− "type":"array",
1269− "items":{
1270− "$ref":"#/definitions/User"
1271− }
1272− }
1273− }
1274− ],
1275− "responses":{
1276− "default":{
1277− "description":"successful operation"
1278− }
1279− }
1280− }
1281− },
1282− "/user/createWithList":{
1283− "post":{
1284− "tags":[
1285− "user"
1286− ],
1287− "summary":"Creates list of users with given input array",
1288− "description":"",
1289− "operationId":"createUsersWithListInput",
1290− "produces":[
1291− "application/xml",
1292− "application/json"
1293− ],
1294− "parameters":[
1295− {
1296− "in":"body",
1297− "name":"body",
1298− "description":"List of user object",
1299− "required":true,
1300− "schema":{
1301− "type":"array",
1302− "items":{
1303− "$ref":"#/definitions/User"
1304− }
1305− }
1306− }
1307− ],
1308− "responses":{
1309− "default":{
1310− "description":"successful operation"
1311− }
1312− }
1313− }
1314− },
1315− "/user/login":{
1316− "get":{
1317− "tags":[
1318− "user"
1319− ],
1320− "summary":"Logs user into the system",
1321− "description":"",
1322− "operationId":"loginUser",
1323− "produces":[
1324− "application/xml",
1325− "application/json"
1326− ],
1327− "parameters":[
1328− {
1329− "name":"username",
1330− "in":"query",
1331− "description":"The user name for login",
1332− "required":true,
1333− "type":"string"
1334− },
1335− {
1336− "name":"password",
1337− "in":"query",
1338− "description":"The password for login in clear text",
1339− "required":true,
1340− "type":"string"
1341− }
1342− ],
1343− "responses":{
1344− "200":{
1345− "description":"successful operation",
1346− "schema":{
1347− "type":"string"
1348− },
1349− "headers":{
1350− "X-Rate-Limit":{
1351− "type":"integer",
1352− "format":"int32",
1353− "description":"calls per hour allowed by the user"
1354− },
1355− "X-Expires-After":{
1356− "type":"string",
1357− "format":"date-time",
1358− "description":"date in UTC when toekn expires"
1359− }
1360− }
1361− },
1362− "400":{
1363− "description":"Invalid username/password supplied"
1364− }
1365− }
1366− }
1367− },
1368− "/user/logout":{
1369− "get":{
1370− "tags":[
1371− "user"
1372− ],
1373− "summary":"Logs out current logged in user session",
1374− "description":"",
1375− "operationId":"logoutUser",
1376− "produces":[
1377− "application/xml",
1378− "application/json"
1379− ],
1380− "parameters": [],
1381− "responses":{
1382− "default":{
1383− "description":"successful operation"
1384− }
1385− }
1386− }
1387− },
1388− "/user/{username}":{
1389− "get":{
1390− "tags":[
1391− "user"
1392− ],
1393− "summary":"Get user by user name",
1394− "description":"",
1395− "operationId":"getUserByName",
1396− "produces":[
1397− "application/xml",
1398− "application/json"
1399− ],
1400− "parameters":[
1401− {
1402− "name":"username",
1403− "in":"path",
1404− "description":"The name that needs to be fetched. Use user1 for testing. ",
1405− "required":true,
1406− "type":"string"
1407− }
1408− ],
1409− "responses":{
1410− "200":{
1411− "description":"successful operation",
1412− "schema":{
1413− "$ref":"#/definitions/User"
1414− }
1415− },
1416− "400":{
1417− "description":"Invalid username supplied"
1418− },
1419− "404":{
1420− "description":"User not found"
1421− }
1422− }
1423− },
1424− "put":{
1425− "tags":[
1426− "user"
1427− ],
1428− "summary":"Updated user",
1429− "description":"This can only be done by the logged in user.",
1430− "operationId":"updateUser",
1431− "produces":[
1432− "application/xml",
1433− "application/json"
1434− ],
1435− "parameters":[
1436− {
1437− "name":"username",
1438− "in":"path",
1439− "description":"name that need to be deleted",
1440− "required":true,
1441− "type":"string"
1442− },
1443− {
1444− "in":"body",
1445− "name":"body",
1446− "description":"Updated user object",
1447− "required":true,
1448− "schema":{
1449− "$ref":"#/definitions/User"
1450− }
1451− }
1452− ],
1453− "responses":{
1454− "400":{
1455− "description":"Invalid user supplied"
1456− },
1457− "404":{
1458− "description":"User not found"
1459− }
1460− }
1461− },
1462− "delete":{
1463− "tags":[
1464− "user"
1465− ],
1466− "summary":"Delete user",
1467− "description":"This can only be done by the logged in user.",
1468− "operationId":"deleteUser",
1469− "produces":[
1470− "application/xml",
1471− "application/json"
1472− ],
1473− "parameters":[
1474− {
1475− "name":"username",
1476− "in":"path",
1477− "description":"The name that needs to be deleted",
1478− "required":true,
1479− "type":"string"
1480− }
1481− ],
1482− "responses":{
1483− "400":{
1484− "description":"Invalid username supplied"
1485− },
1486− "404":{
1487− "description":"User not found"
1488− }
1489− }
1490− }
1491− }
1492− },
1493− "securityDefinitions":{
1494− "petstore_auth":{
1495− "type":"oauth2",
1496− "authorizationUrl":"http://petstore.swagger.io/api/oauth/dialog",
1497− "flow":"implicit",
1498− "scopes":{
1499− "write:pets":"modify pets in your account",
1500− "read:pets":"read your pets"
1501− }
1502− },
1503− "api_key":{
1504− "type":"apiKey",
1505− "name":"api_key",
1506− "in":"header"
1507− }
1508− },
1509− "definitions":{
1510− "Order":{
1511− "type":"object",
1512− "properties":{
1513− "id":{
1514− "type":"integer",
1515− "format":"int64"
1516− },
1517− "petId":{
1518− "type":"integer",
1519− "format":"int64"
1520− },
1521− "quantity":{
1522− "type":"integer",
1523− "format":"int32"
1524− },
1525− "shipDate":{
1526− "type":"string",
1527− "format":"date-time"
1528− },
1529− "status":{
1530− "type":"string",
1531− "description":"Order Status",
1532− "enum":[
1533− "placed",
1534− "approved",
1535− "delivered"
1536− ]
1537− },
1538− "complete":{
1539− "type":"boolean",
1540− "default":false
1541− }
1542− },
1543− "xml":{
1544− "name":"Order"
1545− }
1546− },
1547− "Category":{
1548− "type":"object",
1549− "properties":{
1550− "id":{
1551− "type":"integer",
1552− "format":"int64"
1553− },
1554− "name":{
1555− "type":"string"
1556− }
1557− },
1558− "xml":{
1559− "name":"Category"
1560− }
1561− },
1562− "User":{
1563− "type":"object",
1564− "properties":{
1565− "id":{
1566− "type":"integer",
1567− "format":"int64"
1568− },
1569− "username":{
1570− "type":"string"
1571− },
1572− "firstName":{
1573− "type":"string"
1574− },
1575− "lastName":{
1576− "type":"string"
1577− },
1578− "email":{
1579− "type":"string"
1580− },
1581− "password":{
1582− "type":"string"
1583− },
1584− "phone":{
1585− "type":"string"
1586− },
1587− "userStatus":{
1588− "type":"integer",
1589− "format":"int32",
1590− "description":"User Status"
1591− }
1592− },
1593− "xml":{
1594− "name":"User"
1595− }
1596− },
1597− "Tag":{
1598− "type":"object",
1599− "properties":{
1600− "id":{
1601− "type":"integer",
1602− "format":"int64"
1603− },
1604− "name":{
1605− "type":"string"
1606− }
1607− },
1608− "xml":{
1609− "name":"Tag"
1610− }
1611− },
1612− "Pet":{
1613− "type":"object",
1614− "required":[
1615− "name",
1616− "photoUrls"
1617− ],
1618− "properties":{
1619− "id":{
1620− "type":"integer",
1621− "format":"int64"
1622− },
1623− "category":{
1624− "$ref":"#/definitions/Category"
1625− },
1626− "name":{
1627− "type":"string",
1628− "example":"doggie"
1629− },
1630− "photoUrls":{
1631− "type":"array",
1632− "xml":{
1633− "name":"photoUrl",
1634− "wrapped":true
1635− },
1636− "items":{
1637− "type":"string"
1638− }
1639− },
1640− "tags":{
1641− "type":"array",
1642− "xml":{
1643− "name":"tag",
1644− "wrapped":true
1645− },
1646− "items":{
1647− "$ref":"#/definitions/Tag"
1648− }
1649− },
1650− "status":{
1651− "type":"string",
1652− "description":"pet status in the store",
1653− "enum":[
1654− "available",
1655− "pending",
1656− "sold"
1657− ]
1658− }
1659− },
1660− "xml":{
1661− "name":"Pet"
1662− }
1663− },
1664− "ApiResponse":{
1665− "type":"object",
1666− "properties":{
1667− "code":{
1668− "type":"integer",
1669− "format":"int32"
1670− },
1671− "type":{
1672− "type":"string"
1673− },
1674− "message":{
1675− "type":"string"
1676− }
1677− }
1678− }
1679− },
1680− "externalDocs":{
1681− "description":"Find out more about Swagger",
1682− "url":"http://swagger.io"
1683− }
1684−}
1685−|]
1686−
1687−compositionSchemaExample :: Schema
1688−compositionSchemaExample = mempty
1689− & type_ ?~ SwaggerObject
1690− & Data.Swagger.allOf ?~ [
1691− Ref (Reference "Other")
1692− , Inline (mempty
1693− & type_ ?~ SwaggerObject
1694− & properties .~
1695− [ ("greet", Inline $ mempty
1696− & type_ ?~ SwaggerString) ])
1697− ]
1698−
1699−compositionSchemaExampleJSON :: Value
1700−compositionSchemaExampleJSON = [aesonQQ|
1701−{
1702− "type": "object",
1703− "allOf": [
1704− {
1705− "$ref": "#/definitions/Other"
1706− },
1707− {
1708− "type": "object",
1709− "properties": {
1710− "greet": { "type": "string" }
1711− }
1712− }
1713− ]
1714−}
1715−|]
test/SpecCommon.hs+1−9
@@ -8,15 +8,7 @@ import qualified Data.Vector as Vector
8
8
9import Test.Hspec
9import Test.Hspec
10
10
11−isSubJSON :: Value -> Value -> Bool
12−isSubJSON Null _ = True
13−isSubJSON (Object x) (Object y) = HashMap.keys x == HashMap.keys i && F.and i
14− where
15− i = HashMap.intersectionWith isSubJSON x y
16−isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)
17−isSubJSON x y = x == y
18−
19−(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec
11+(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a, HasCallStack) => a -> Value -> Spec
20x <=> js = do
12x <=> js = do
21 it "encodes correctly" $ do
13 it "encodes correctly" $ do
22 toJSON x `shouldBe` js
14 toJSON x `shouldBe` js