git.iko.soy/typeable/openapi3
on 5b5cfcb help sign in
37 files changed+3967−4043
since splitsnapshots
unifiedsplit
.github/workflows/haskell-ci.yml−243 .travis.yml+155 CHANGELOG.md−47 README.md+11−13 cabal.haskell-ci−11 cabal.project−2 examples/hackage.hs+20−16 src/Data/OpenApi/Aeson/Compat.hs−76 src/Data/OpenApi/Lens.hs−166 src/Data/OpenApi/Optics.hs−328 src/Data/OpenApi.hs → src/Data/Swagger.hs+97−189 src/Data/OpenApi/Declare.hs → src/Data/Swagger/Declare.hs+2−2 src/Data/OpenApi/Internal.hs → src/Data/Swagger/Internal.hs+558−554 src/Data/OpenApi/Internal/AesonUtils.hs → src/Data/Swagger/Internal/AesonUtils.hs+13−12 src/Data/OpenApi/Internal/ParamSchema.hs → src/Data/Swagger/Internal/ParamSchema.hs+53−77 src/Data/OpenApi/Internal/Schema.hs → src/Data/Swagger/Internal/Schema.hs+172−446 src/Data/OpenApi/Internal/Schema/Validation.hs → src/Data/Swagger/Internal/Schema/Validation.hs+98−119 src/Data/OpenApi/Internal/TypeShape.hs → src/Data/Swagger/Internal/TypeShape.hs+1−1 src/Data/OpenApi/Internal/Utils.hs → src/Data/Swagger/Internal/Utils.hs+1−6 src/Data/Swagger/Lens.hs+187 src/Data/OpenApi/Operation.hs → src/Data/Swagger/Operation.hs+31−140 src/Data/Swagger/Optics.hs+605 src/Data/OpenApi/ParamSchema.hs → src/Data/Swagger/ParamSchema.hs+7−7 src/Data/OpenApi/Schema.hs → src/Data/Swagger/Schema.hs+13−4 src/Data/OpenApi/Schema/Generator.hs → src/Data/Swagger/Schema/Generator.hs+19−23 src/Data/OpenApi/Schema/Validation.hs → src/Data/Swagger/Schema/Validation.hs+7−7 src/Data/OpenApi/SchemaOptions.hs → src/Data/Swagger/SchemaOptions.hs+3−7 stack.yaml+1−1 openapi3.cabal → swagger2.cabal+57−65 test/Data/OpenApiSpec.hs−962 test/Data/OpenApi/CommonTestTypes.hs → test/Data/Swagger/CommonTestTypes.hs+100−436 test/Data/OpenApi/ParamSchemaSpec.hs → test/Data/Swagger/ParamSchemaSpec.hs+5−4 test/Data/OpenApi/Schema/GeneratorSpec.hs → test/Data/Swagger/Schema/GeneratorSpec.hs+7−7 test/Data/OpenApi/Schema/ValidationSpec.hs → test/Data/Swagger/Schema/ValidationSpec.hs+9−56 test/Data/OpenApi/SchemaSpec.hs → test/Data/Swagger/SchemaSpec.hs+11−15 test/Data/SwaggerSpec.hs+1715 test/SpecCommon.hs+9−1
.github/workflows/haskell-ci.ymldeleted−243
@@ -1,243 +0,0 @@
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.ymlnew file+155
@@ -0,0 +1,155 @@
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,50 +1,3 @@
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−
4812.6.1
492-----
503
README.md+11−13
@@ -1,22 +1,21 @@
1−# OpenApi 3
1+# swagger2
22
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)
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)
74
8−OpenAPI 3.0 data model.
5+[![Hackage](https://img.shields.io/hackage/v/swagger2.svg)](http://hackage.haskell.org/package/swagger2)
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)
99
10−The original OpenAPI 3.0 specification is available at http://swagger.io/specification/.
10+Swagger 2.0 data model.
1111
12−This package is heavily based on excellent work on Swagger 2.0 at
13−https://github.com/GetShopTV/swagger2.
12+The original Swagger 2.0 specification is available at http://swagger.io/specification/.
1413
1514## Usage
1615
17−This library is intended to be used for decoding and encoding OpenApi 3.0.3 specifications as well as manipulating them.
16+This library is intended to be used for decoding and encoding Swagger 2.0 API specifications as well as manipulating them.
1817
19−Please refer to [haddock documentation](http://hackage.haskell.org/package/openapi3).
18+Please refer to [haddock documentation](http://hackage.haskell.org/package/swagger2).
2019
2120Some examples can be found in [`examples/` directory](/examples).
2221
@@ -34,8 +33,7 @@ Many Swagger tools, including server and client code generation for many languag
3433
3534We are happy to receive bug reports, fixes, documentation enhancements, and other improvements.
3635
37−Please report bugs via the [github issue tracker](https://github.com/biocad/openapi3/issues).
36+Please report bugs via the [github issue tracker](https://github.com/GetShopTV/swagger2/issues).
3837
3938*GetShopTV Team*
4039
41−*Biocad Team*
cabal.haskell-cideleted−11
@@ -1,11 +0,0 @@
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,4 +1,2 @@
11packages: .
22tests: true
3−
4−allow-newer: http-media:base
examples/hackage.hs+20−16
@@ -10,10 +10,10 @@ import Data.Proxy
1010import Data.Text (Text)
1111import GHC.Generics
1212
13−import Data.OpenApi
14−import Data.OpenApi.Declare
15−import Data.OpenApi.Lens
16−import Data.OpenApi.Operation
13+import Data.Swagger
14+import Data.Swagger.Declare
15+import Data.Swagger.Lens
16+import Data.Swagger.Operation
1717
1818type Username = Text
1919
@@ -27,7 +27,7 @@ instance ToSchema UserSummary where
2727 usernameSchema <- declareSchemaRef (Proxy :: Proxy Username)
2828 useridSchema <- declareSchemaRef (Proxy :: Proxy Int)
2929 return $ NamedSchema (Just "UserSummary") $ mempty
30− & type_ ?~ OpenApiObject
30+ & type_ ?~ SwaggerObject
3131 & properties .~
3232 [ ("summaryUsername", usernameSchema )
3333 , ("summaryUserid" , useridSchema )
@@ -47,36 +47,40 @@ data UserDetailed = UserDetailed
4747newtype Package = Package { packageName :: Text }
4848 deriving (Generic, ToSchema)
4949
50−hackageOpenApi :: OpenApi
51−hackageOpenApi = spec & components.schemas .~ defs
50+hackageSwagger :: Swagger
51+hackageSwagger = spec & definitions .~ defs
5252 where
53− (defs, spec) = runDeclare declareHackageOpenApi mempty
53+ (defs, spec) = runDeclare declareHackageSwagger mempty
5454
55−declareHackageOpenApi :: Declare (Definitions Schema) OpenApi
56−declareHackageOpenApi = do
55+declareHackageSwagger :: Declare (Definitions Schema) Swagger
56+declareHackageSwagger = do
5757 -- param schemas
5858 let usernameParamSchema = toParamSchema (Proxy :: Proxy Username)
5959
6060 -- responses
61− userSummaryResponse <- declareResponse "application/json" (Proxy :: Proxy UserSummary)
62− userDetailedResponse <- declareResponse "application/json" (Proxy :: Proxy UserDetailed)
63− packagesResponse <- declareResponse "application/json" (Proxy :: Proxy [Package])
61+ userSummaryResponse <- declareResponse (Proxy :: Proxy UserSummary)
62+ userDetailedResponse <- declareResponse (Proxy :: Proxy UserDetailed)
63+ packagesResponse <- declareResponse (Proxy :: Proxy [Package])
6464
6565 return $ mempty
6666 & paths .~
6767 [ ("/users", mempty & get ?~ (mempty
68+ & produces ?~ MimeList ["application/json"]
6869 & at 200 ?~ Inline userSummaryResponse))
6970 , ("/user/{username}", mempty & get ?~ (mempty
71+ & produces ?~ MimeList ["application/json"]
7072 & parameters .~ [ Inline $ mempty
7173 & name .~ "username"
7274 & required ?~ True
73− & in_ .~ ParamPath
74− & schema ?~ Inline usernameParamSchema ]
75+ & schema .~ ParamOther (mempty
76+ & in_ .~ ParamPath
77+ & paramSchema .~ usernameParamSchema) ]
7578 & at 200 ?~ Inline userDetailedResponse))
7679 , ("/packages", mempty & get ?~ (mempty
80+ & produces ?~ MimeList ["application/json"]
7781 & at 200 ?~ Inline packagesResponse))
7882 ]
7983
8084main :: IO ()
81−main = putStrLn . read . show . encode $ hackageOpenApi
85+main = putStrLn . read . show . encode $ hackageSwagger
8286
src/Data/OpenApi/Aeson/Compat.hsdeleted−76
@@ -1,76 +0,0 @@
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/OpenApi/Lens.hsdeleted−166
@@ -1,166 +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.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/OpenApi/Optics.hsdeleted−328
@@ -1,328 +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.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/OpenApi.hs → src/Data/Swagger.hsrenamed+97−189
@@ -1,5 +1,5 @@
11-- |
2−-- Module: Data.OpenApi
2+-- Module: Data.Swagger
33-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
44-- Stability: experimental
55--
@@ -9,7 +9,7 @@
99-- These files can then be used by the Swagger-UI project to display the API
1010-- and Swagger-Codegen to generate clients in various languages.
1111-- Additional utilities can also take advantage of the resulting files, such as testing tools.
12−module Data.OpenApi (
12+module Data.Swagger (
1313 -- * How to use this library
1414 -- $howto
1515
@@ -29,15 +29,17 @@ module Data.OpenApi (
2929 -- $validation
3030
3131 -- * Re-exports
32− module Data.OpenApi.Lens,
33− module Data.OpenApi.Optics,
34− module Data.OpenApi.Operation,
35− module Data.OpenApi.ParamSchema,
36− module Data.OpenApi.Schema,
37− module Data.OpenApi.Schema.Validation,
32+ module Data.Swagger.Lens,
33+ module Data.Swagger.Optics,
34+ module Data.Swagger.Operation,
35+ module Data.Swagger.ParamSchema,
36+ module Data.Swagger.Schema,
37+ module Data.Swagger.Schema.Validation,
3838
3939 -- * Swagger specification
40− OpenApi(..),
40+ Swagger(..),
41+ Host(..),
42+ Scheme(..),
4143 Server(..),
4244 ServerVariable(..),
4345 Components(..),
@@ -56,13 +58,15 @@ module Data.OpenApi (
5658 TagName,
5759
5860 -- ** Types and formats
59− OpenApiType(..),
61+ SwaggerType(..),
6062 Format,
6163 Definitions,
62− Style(..),
64+ CollectionFormat(..),
6365
6466 -- ** Parameters
6567 Param(..),
68+ ParamAnySchema(..),
69+ ParamOtherSchema(..),
6670 ParamLocation(..),
6771 ParamName,
6872 Header(..),
@@ -73,9 +77,10 @@ module Data.OpenApi (
7377 Encoding(..),
7478
7579 -- ** Schemas
80+ ParamSchema(..),
7681 Schema(..),
7782 NamedSchema(..),
78− OpenApiItems(..),
83+ SwaggerItems(..),
7984 Xml(..),
8085 Pattern,
8186 AdditionalProperties(..),
@@ -85,27 +90,20 @@ module Data.OpenApi (
8590 Responses(..),
8691 Response(..),
8792 HttpStatusCode,
88− Link(..),
89− Callback(..),
9093
9194 -- ** Security
9295 SecurityScheme(..),
9396 SecuritySchemeType(..),
94− HttpSchemeType(..),
95− SecurityDefinitions(..),
9697 SecurityRequirement(..),
98+ SecurityDefinitions(..),
9799
98100 -- *** API key
99101 ApiKeyParams(..),
100102 ApiKeyLocation(..),
101103
102104 -- *** OAuth2
103− OAuth2Flows(..),
105+ OAuth2Params(..),
104106 OAuth2Flow(..),
105− OAuth2ImplicitFlow(..),
106− OAuth2PasswordFlow(..),
107− OAuth2ClientCredentialsFlow(..),
108− OAuth2AuthorizationCodeFlow(..),
109107 AuthorizationURL,
110108 TokenURL,
111109
@@ -121,14 +119,14 @@ module Data.OpenApi (
121119 URL(..),
122120) where
123121
124−import Data.OpenApi.Lens
125−import Data.OpenApi.Optics ()
126−import Data.OpenApi.Operation
127−import Data.OpenApi.ParamSchema
128−import Data.OpenApi.Schema
129−import Data.OpenApi.Schema.Validation
122+import Data.Swagger.Lens
123+import Data.Swagger.Optics ()
124+import Data.Swagger.Operation
125+import Data.Swagger.ParamSchema
126+import Data.Swagger.Schema
127+import Data.Swagger.Schema.Validation
130128
131−import Data.OpenApi.Internal
129+import Data.Swagger.Internal
132130
133131-- $setup
134132-- >>> import Control.Lens
@@ -136,8 +134,6 @@ import Data.OpenApi.Internal
136134-- >>> import Data.Monoid
137135-- >>> import Data.Proxy
138136-- >>> import GHC.Generics
139−-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
140−-- >>> import Data.OpenApi.Internal.Utils
141137-- >>> :set -XDeriveGeneric
142138-- >>> :set -XOverloadedStrings
143139-- >>> :set -XOverloadedLists
@@ -154,16 +150,8 @@ import Data.OpenApi.Internal
154150--
155151-- In this library you can use @'mempty'@ for a default/empty value. For instance:
156152--
157−-- >>> BSL.putStrLn $ encodePretty (mempty :: OpenApi)
158−-- {
159−-- "components": {},
160−-- "info": {
161−-- "title": "",
162−-- "version": ""
163−-- },
164−-- "openapi": "3.0.0",
165−-- "paths": {}
166−-- }
153+-- >>> encode (mempty :: Swagger)
154+-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"}}"
167155--
168156-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
169157-- That is because these properties are actually required ones.
@@ -171,87 +159,47 @@ import Data.OpenApi.Internal
171159-- You /should/ always override the default (empty) value for these properties,
172160-- although it is not strictly necessary:
173161--
174−-- >>> BSL.putStrLn $ encodePretty mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
175−-- {
176−-- "title": "Todo API",
177−-- "version": "1.0"
178−-- }
162+-- >>> encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
163+-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
179164--
180165-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
181166--
182−-- >>> BSL.putStrLn $ encodePretty $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
183−-- {
184−-- "title": "Todo API",
185−-- "version": "1.0"
186−-- }
187−
167+-- >>> encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
168+-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
188169--
189170-- This can be useful for combining specifications of endpoints into a whole API specification:
190171--
191172-- @
192173-- \-\- /account subAPI specification
193−-- accountAPI :: OpenApi
174+-- accountAPI :: Swagger
194175--
195176-- \-\- /task subAPI specification
196−-- taskAPI :: OpenApi
177+-- taskAPI :: Swagger
197178--
198179-- \-\- while API specification is just a combination
199180-- \-\- of subAPIs' specifications
200−-- api :: OpenApi
181+-- api :: Swagger
201182-- api = accountAPI <> taskAPI
202183-- @
203184
204185-- $lens
205186--
206−-- Note: if you're working with the <https://hackage.haskell.org/package/optics optics> library, take a look at "Data.OpenApi.Optics".
187+-- Note: if you're working with the <https://hackage.haskell.org/package/optics optics> library, take a look at "Data.Swagger.Optics".
207188--
208189-- Since @'Swagger'@ has a fairly complex structure, lenses and prisms are used
209190-- to work comfortably with it. In combination with @'Monoid'@ instances, lenses
210191-- make it fairly simple to construct/modify any part of the specification:
211192--
212193-- >>> :{
213−-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
214−-- & components . schemas .~ [ ("User", mempty & type_ ?~ OpenApiString) ]
194+-- encode $ (mempty :: Swagger)
195+-- & definitions .~ [ ("User", mempty & type_ ?~ SwaggerString) ]
215196-- & paths .~
216197-- [ ("/user", mempty & get ?~ (mempty
217−-- & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User")))
198+-- & produces ?~ MimeList ["application/json"]
199+-- & at 200 ?~ ("OK" & _Inline.schema ?~ Ref (Reference "User"))
218200-- & at 404 ?~ "User info not found")) ]
219201-- :}
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+-- "{\"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\"}}}"
255203--
256204-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
257205-- which produces @application/json@ output. It should respond with code @200@ and body specified
@@ -263,34 +211,30 @@ import Data.OpenApi.Internal
263211-- common field is @'description'@. Many components of a Swagger specification
264212-- can have descriptions, and you can use the same name for them:
265213--
266−-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & description .~ "No content"
267−-- {
268−-- "description": "No content"
269−-- }
214+-- >>> encode $ (mempty :: Response) & description .~ "No content"
215+-- "{\"description\":\"No content\"}"
270216-- >>> :{
271−-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
272−-- & type_ ?~ OpenApiBoolean
217+-- encode $ (mempty :: Schema)
218+-- & type_ ?~ SwaggerBoolean
273219-- & description ?~ "To be or not to be"
274220-- :}
275−-- {
276−-- "description": "To be or not to be",
277−-- "type": "boolean"
278−-- }
221+-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
222+--
223+-- @'ParamSchema'@ is basically the /base schema specification/ and many types contain it (see @'HasParamSchema'@).
224+-- So for convenience, all @'ParamSchema'@ fields are transitively made fields of the type that has it.
225+-- For example, you can use @'type_'@ to access @'SwaggerType'@ of @'Header'@ schema without having to use @'paramSchema'@:
226+--
227+-- >>> encode $ (mempty :: Header) & type_ ?~ SwaggerNumber
228+-- "{\"type\":\"number\"}"
279229--
280230-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
281231-- have direct access to it via @'at' code@. Example:
282232--
283233-- >>> :{
284−-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
234+-- encode $ (mempty :: Operation)
285235-- & at 404 ?~ "Not found"
286236-- :}
287−-- {
288−-- "responses": {
289−-- "404": {
290−-- "description": "Not found"
291−-- }
292−-- }
293−-- }
237+-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
294238--
295239-- You might've noticed that @'type_'@ has an extra underscore in its name
296240-- compared to, say, @'description'@ field accessor.
@@ -306,7 +250,7 @@ import Data.OpenApi.Internal
306250-- @'ParamSchema'@ and @'Schema'@ are the two core types for data model specification.
307251--
308252-- @'ParamSchema' t@ specifies all the common properties, available for every data schema.
309−-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'OpenApiType'@ and @'OpenApiItems'@).
253+-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'SwaggerType'@ and @'SwaggerItems'@).
310254--
311255-- @'Schema'@ is used for request and response bodies and allows specifying objects
312256-- with properties in addition to what @'ParamSchema'@ provides.
@@ -337,81 +281,45 @@ import Data.OpenApi.Internal
337281-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
338282-- >>> instance ToJSON Person
339283-- >>> instance ToSchema Person
340−-- >>> BSL.putStrLn $ encodePretty (Person "David" 28)
341−-- {
342−-- "age": 28,
343−-- "name": "David"
344−-- }
345−-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Person)
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−-- }
361−--
362−-- This package implements OpenAPI 3.0 spec, which supports @oneOf@ in schemas, allowing any sum types
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:
367−--
368−-- >>> data Error = ErrorNoUser { userId :: Int } | ErrorAccessDenied { requiredPermission :: String } deriving Generic
369−-- >>> instance ToJSON Error
370−-- >>> instance ToSchema Error
371−-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Error)
372−-- {
373−-- "oneOf": [
374−-- {
375−-- "properties": {
376−-- "tag": {
377−-- "enum": [
378−-- "ErrorNoUser"
379−-- ],
380−-- "type": "string"
381−-- },
382−-- "userId": {
383−-- "maximum": 9223372036854775807,
384−-- "minimum": -9223372036854775808,
385−-- "type": "integer"
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"
402−-- ],
403−-- "type": "string"
404−-- }
405−-- },
406−-- "required": [
407−-- "requiredPermission",
408−-- "tag"
409−-- ],
410−-- "type": "object"
411−-- }
412−-- ],
413−-- "type": "object"
414−-- }
284+-- >>> encode (Person "David" 28)
285+-- "{\"age\":28,\"name\":\"David\"}"
286+-- >>> encode $ toSchema (Proxy :: Proxy Person)
287+-- "{\"required\":[\"name\",\"age\"],\"properties\":{\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"}},\"type\":\"object\"}"
288+--
289+-- Please note that not all valid Haskell data types will have a proper swagger schema. For example while we can derive a
290+-- schema for basic enums like
291+--
292+-- >>> data SampleEnum = ChoiceOne | ChoiceTwo deriving Generic
293+-- >>> instance ToSchema SampleEnum
294+-- >>> instance ToJSON SampleEnum
295+--
296+-- and for sum types that have constructors with values
297+--
298+-- >>> data SampleSumType = ChoiceInt Int | ChoiceString String deriving Generic
299+-- >>> instance ToSchema SampleSumType
300+-- >>> instance ToJSON SampleSumType
301+--
302+-- we can not derive a valid schema for a mix of the above. The following will result in a type error
303+--
304+-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
305+-- >>> instance ToSchema BadMixedType
306+-- ...
307+-- ... error:
308+-- ... • Cannot derive Generic-based Swagger Schema for BadMixedType
309+-- ... BadMixedType is a mixed sum type (has both unit and non-unit constructors).
310+-- ... Swagger does not have a good representation for these types.
311+-- ... Use genericDeclareNamedSchemaUnrestricted if you want to derive schema
312+-- ... that matches aeson's Generic-based toJSON,
313+-- ... but that's not supported by some Swagger tools.
314+-- ...
315+-- ... In the instance declaration for ‘ToSchema BadMixedType’
316+--
317+-- We can use 'genericDeclareNamedSchemaUnrestricted' to try our best to represent this type as a Swagger Schema and match 'ToJSON':
318+--
319+-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
320+-- >>> instance ToSchema BadMixedType where declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
321+-- >>> instance ToJSON BadMixedType
322+--
415323
416324-- $manipulation
417325-- Sometimes you have to work with an imported or generated @'Swagger'@.
@@ -419,10 +327,10 @@ import Data.OpenApi.Internal
419327-- for a type-level servant API.
420328--
421329-- Lenses and prisms can be used to manipulate such specification to add additional information, tags, extra responses, etc.
422−-- To facilitate common needs, @"Data.OpenApi.Operation"@ module provides useful helpers.
330+-- To facilitate common needs, @"Data.Swagger.Operation"@ module provides useful helpers.
423331
424332-- $validation
425333-- While @'ToParamSchema'@ and @'ToSchema'@ provide means to easily obtain schemas for Haskell types,
426334-- there is no static mechanism to ensure those instances correspond to the @'ToHttpApiData'@ or @'ToJSON'@ instances.
427335--
428−-- @"Data.OpenApi.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.
336+-- @"Data.Swagger.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.
src/Data/OpenApi/Declare.hs → src/Data/Swagger/Declare.hsrenamed+2−2
@@ -4,12 +4,12 @@
44{-# LANGUAGE MultiParamTypeClasses #-}
55{-# LANGUAGE UndecidableInstances #-}
66-- |
7−-- Module: Data.OpenApi.Declare
7+-- Module: Data.Swagger.Declare
88-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
99-- Stability: experimental
1010--
1111-- Declare monad transformer and associated functions.
12−module Data.OpenApi.Declare where
12+module Data.Swagger.Declare where
1313
1414import Prelude ()
1515import Prelude.Compat
src/Data/OpenApi/Internal.hs → src/Data/Swagger/Internal.hsrenamed+558−554
@@ -1,6 +1,6 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DataKinds #-}
32{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveFunctor #-}
44{-# LANGUAGE DeriveGeneric #-}
55{-# LANGUAGE DeriveTraversable #-}
66{-# LANGUAGE FlexibleContexts #-}
@@ -14,91 +14,87 @@
1414{-# LANGUAGE TemplateHaskell #-}
1515{-# LANGUAGE TypeFamilies #-}
1616{-# LANGUAGE UndecidableInstances #-}
17−module Data.OpenApi.Internal where
17+module Data.Swagger.Internal where
1818
1919import Prelude ()
2020import Prelude.Compat
2121
22+import Control.Lens ((&), (.~), (?~))
2223import Control.Applicative
23−import Control.Lens ((&), (.~), (?~))
24−import Data.Aeson hiding (Encoding)
25−#if MIN_VERSION_aeson(2,0,0)
26−import qualified Data.Aeson.KeyMap as KeyMap
27−#endif
28−import qualified Data.Aeson.Types as JSON
29−import Data.Data (Constr, Data (..), DataType, Fixity (..), Typeable,
30− constrIndex, mkConstr, mkDataType)
31−import Data.Hashable (Hashable (..))
32−import qualified Data.HashMap.Strict as HashMap
33−import Data.HashSet.InsOrd (InsOrdHashSet)
34−import Data.Map (Map)
35−import qualified Data.Map as Map
36−import Data.Monoid (Monoid (..))
37−import Data.Scientific (Scientific)
38−import Data.Semigroup.Compat (Semigroup (..))
39−import Data.String (IsString (..))
40−import Data.Text (Text)
41−import qualified Data.Text as Text
42−import Data.Text.Encoding (encodeUtf8)
43−import GHC.Generics (Generic)
44−import Network.HTTP.Media (MediaType, mainType, parameters, parseAccept, subType, (//),
45− (/:))
46−import Network.Socket (HostName, PortNumber)
47−import Text.Read (readMaybe)
24+import Data.Aeson hiding (Encoding)
25+import qualified Data.Aeson.Types as JSON
26+import Data.Data (Data(..), Typeable, mkConstr, mkDataType, Fixity(..), Constr, DataType, constrIndex)
27+import Data.Hashable (Hashable)
28+import qualified Data.HashMap.Strict as HashMap
29+import Data.HashSet.InsOrd (InsOrdHashSet)
30+import Data.Map (Map)
31+import qualified Data.Map as Map
32+import Data.Monoid (Monoid (..))
33+import Data.Semigroup.Compat (Semigroup (..))
34+import Data.Scientific (Scientific)
35+import Data.String (IsString(..))
36+import Data.Text (Text)
37+import qualified Data.Text as Text
38+import GHC.Generics (Generic)
39+import Network.Socket (HostName, PortNumber)
40+import Network.HTTP.Media (MediaType)
41+import Text.Read (readMaybe)
4842
4943import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
5044import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
5145
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
5846import 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)
5957
6058-- $setup
6159-- >>> :seti -XDataKinds
6260-- >>> import Data.Aeson
63−-- >>> import Data.ByteString.Lazy.Char8 as BSL
64−-- >>> import Data.OpenApi.Internal.Utils
6561
6662-- | A list of definitions that can be used in references.
6763type Definitions = InsOrdHashMap Text
6864
6965-- | This is the root document object for the API specification.
70−data OpenApi = OpenApi
66+data Swagger = Swagger
7167 { -- | Provides metadata about the API.
7268 -- The metadata can be used by the clients if needed.
73− _openApiInfo :: Info
69+ _swaggerInfo :: Info
7470
7571 -- | An array of Server Objects, which provide connectivity information
7672 -- to a target server. If the servers property is not provided, or is an empty array,
7773 -- the default value would be a 'Server' object with a url value of @/@.
78− , _openApiServers :: [Server]
74+ , _swaggerServers :: [Server]
7975
8076 -- | The available paths and operations for the API.
81− , _openApiPaths :: InsOrdHashMap FilePath PathItem
77+ , _swaggerPaths :: InsOrdHashMap FilePath PathItem
8278
8379 -- | An element to hold various schemas for the specification.
84− , _openApiComponents :: Components
80+ , _swaggerComponents :: Components
8581
8682 -- | A declaration of which security mechanisms can be used across the API.
8783 -- The list of values includes alternative security requirement objects that can be used.
8884 -- Only one of the security requirement objects need to be satisfied to authorize a request.
8985 -- Individual operations can override this definition.
9086 -- To make security optional, an empty security requirement can be included in the array.
91− , _openApiSecurity :: [SecurityRequirement]
87+ , _swaggerSecurity :: [SecurityRequirement]
9288
9389 -- | A list of tags used by the specification with additional metadata.
9490 -- The order of the tags can be used to reflect on their order by the parsing tools.
9591 -- Not all tags that are used by the 'Operation' Object must be declared.
9692 -- The tags that are not declared MAY be organized randomly or based on the tools' logic.
9793 -- Each tag name in the list MUST be unique.
98− , _openApiTags :: InsOrdHashSet Tag
94+ , _swaggerTags :: InsOrdHashSet Tag
9995
10096 -- | Additional external documentation.
101− , _openApiExternalDocs :: Maybe ExternalDocs
97+ , _swaggerExternalDocs :: Maybe ExternalDocs
10298 } deriving (Eq, Show, Generic, Data, Typeable)
10399
104100-- | The object provides metadata about the API.
@@ -183,9 +179,6 @@ data ServerVariable = ServerVariable
183179 , _serverVariableDescription :: Maybe Text
184180 } deriving (Eq, Show, Generic, Data, Typeable)
185181
186−instance IsString Server where
187− fromString s = Server (fromString s) Nothing mempty
188−
189182-- | Holds a set of reusable objects for different aspects of the OAS.
190183-- All objects defined within the components object will have no effect on the API
191184-- unless they are explicitly referenced from properties outside the components object.
@@ -195,12 +188,42 @@ data Components = Components
195188 , _componentsParameters :: Definitions Param
196189 , _componentsExamples :: Definitions Example
197190 , _componentsRequestBodies :: Definitions RequestBody
198− , _componentsHeaders :: Definitions Header
199− , _componentsSecuritySchemes :: SecurityDefinitions
200− , _componentsLinks :: Definitions Link
201− , _componentsCallbacks :: Definitions Callback
191+ , _componentsHeader :: Definitions Header
192+ , _componentsSecuritySchemes :: Definitions SecurityScheme
193+-- , _componentsLinks
194+-- , _componentsCallbacks
202195 } deriving (Eq, Show, Generic, Data, Typeable)
203196
197+-- | The host (name or ip) serving the API. It MAY include a port.
198+data Host = Host
199+ { _hostName :: HostName -- ^ Host name.
200+ , _hostPort :: Maybe PortNumber -- ^ Optional port.
201+ } deriving (Eq, Show, Generic, Typeable)
202+
203+instance IsString Host where
204+ fromString s = Host s Nothing
205+
206+hostConstr :: Constr
207+hostConstr = mkConstr hostDataType "Host" [] Prefix
208+
209+hostDataType :: DataType
210+hostDataType = mkDataType "Data.Swagger.Host" [hostConstr]
211+
212+instance Data Host where
213+ gunfold k z c = case constrIndex c of
214+ 1 -> k (k (z (\name mport -> Host name (fromInteger <$> mport))))
215+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type Host."
216+ toConstr (Host _ _) = hostConstr
217+ dataTypeOf _ = hostDataType
218+
219+-- | The transfer protocol of the API.
220+data Scheme
221+ = Http
222+ | Https
223+ | Ws
224+ | Wss
225+ deriving (Eq, Show, Generic, Data, Typeable)
226+
204227-- | Describes the operations available on a single path.
205228-- A @'PathItem'@ may be empty, due to ACL constraints.
206229-- The path itself is still exposed to the documentation viewer
@@ -288,11 +311,7 @@ data Operation = Operation
288311 -- | The list of possible responses as they are returned from executing this operation.
289312 , _operationResponses :: Responses
290313
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)
314+ -- TODO callbacks
296315
297316 -- | Declares this operation to be deprecated.
298317 -- Usage of the declared operation should be refrained.
@@ -312,22 +331,6 @@ data Operation = Operation
312331 , _operationServers :: [Server]
313332 } deriving (Eq, Show, Generic, Data, Typeable)
314333
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−
331334-- | Describes a single request body.
332335data RequestBody = RequestBody
333336 { -- | A brief description of the request body. This could contain examples of use.
@@ -337,11 +340,9 @@ data RequestBody = RequestBody
337340 -- | The content of the request body.
338341 -- The key is a media type or media type range and the value describes it.
339342 -- For requests that match multiple keys, only the most specific key is applicable.
340− -- e.g. @text/plain@ overrides @text/\*@
341− , _requestBodyContent :: InsOrdHashMap MediaType MediaTypeObject
343+ -- e.g. @text/plain@ overrides @text/*@
344+ , _requestBodyContent :: InsOrdHashMap {-MediaType-} Text MediaTypeObject -- FIXME Data MediaType
342345
343− -- | Determines if the request body is required in the request.
344− -- Defaults to 'False'.
345346 , _requestBodyRequired :: Maybe Bool
346347 } deriving (Eq, Show, Generic, Data, Typeable)
347348
@@ -364,29 +365,6 @@ data MediaTypeObject = MediaTypeObject
364365 , _mediaTypeObjectEncoding :: InsOrdHashMap Text Encoding
365366 } deriving (Eq, Show, Generic, Data, Typeable)
366367
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−
390368data Encoding = Encoding
391369 { -- | The Content-Type for encoding a specific property.
392370 -- Default value depends on the property type: for @string@
@@ -394,8 +372,8 @@ data Encoding = Encoding
394372 -- for other primitive types – @text/plain@; for object - @application/json@;
395373 -- for array – the default is defined based on the inner type.
396374 -- The value can be a specific media type (e.g. @application/json@),
397− -- a wildcard media type (e.g. @image/\*@), or a comma-separated list of the two types.
398− _encodingContentType :: Maybe MediaType
375+ -- a wildcard media type (e.g. @image/*@), or a comma-separated list of the two types.
376+ _encodingContentType :: Maybe Text
399377
400378 -- | A map allowing additional information to be provided as headers,
401379 -- for example @Content-Disposition@. @Content-Type@ is described separately
@@ -404,11 +382,11 @@ data Encoding = Encoding
404382 , _encodingHeaders :: InsOrdHashMap Text (Referenced Header)
405383
406384 -- | Describes how a specific property value will be serialized depending on its type.
407− -- See 'Param' Object for details on the style property.
385+ -- See Parameter Object for details on the style property.
408386 -- The behavior follows the same values as query parameters, including default values.
409387 -- This property SHALL be ignored if the request body media type
410388 -- is not @application/x-www-form-urlencoded@.
411− , _encodingStyle :: Maybe Style
389+ , _encodingStyle :: Maybe Text -- TODO enum
412390
413391 -- | When this is true, property values of type @array@ or @object@ generate
414392 -- separate parameters for each value of the array,
@@ -434,11 +412,11 @@ mimeListConstr :: Constr
434412mimeListConstr = mkConstr mimeListDataType "MimeList" ["getMimeList"] Prefix
435413
436414mimeListDataType :: DataType
437−mimeListDataType = mkDataType "Data.OpenApi.MimeList" [mimeListConstr]
415+mimeListDataType = mkDataType "Data.Swagger.MimeList" [mimeListConstr]
438416
439417instance Data MimeList where
440418 gunfold k z c = case constrIndex c of
441− 1 -> k (z (MimeList . map fromString))
419+ 1 -> k (z (\xs -> MimeList (map fromString xs)))
442420 _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MimeList."
443421 toConstr (MimeList _) = mimeListConstr
444422 dataTypeOf _ = mimeListDataType
@@ -452,7 +430,7 @@ data Param = Param
452430
453431 -- | A brief description of the parameter.
454432 -- This could contain examples of use.
455− -- CommonMark syntax MAY be used for rich text representation.
433+ -- GFM syntax can be used for rich text representation.
456434 , _paramDescription :: Maybe Text
457435
458436 -- | Determines whether this parameter is mandatory.
@@ -460,60 +438,27 @@ data Param = Param
460438 -- Otherwise, the property MAY be included and its default value is @False@.
461439 , _paramRequired :: Maybe Bool
462440
463− -- | Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
464− -- Default value is @false@.
465− , _paramDeprecated :: Maybe Bool
441+ -- | Parameter schema.
442+ , _paramSchema :: ParamAnySchema
443+ } deriving (Eq, Show, Generic, Data, Typeable)
444+
445+data ParamAnySchema
446+ = ParamBody (Referenced Schema)
447+ | ParamOther ParamOtherSchema
448+ deriving (Eq, Show, Generic, Data, Typeable)
466449
467− -- | The location of the parameter.
468− , _paramIn :: ParamLocation
450+data ParamOtherSchema = ParamOtherSchema
451+ { -- | The location of the parameter.
452+ _paramOtherSchemaIn :: ParamLocation
469453
470454 -- | Sets the ability to pass empty-valued parameters.
471− -- This is valid only for 'ParamQuery' parameters and allows sending
472− -- a parameter with an empty value. Default value is @false@.
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
455+ -- This is valid only for either @'ParamQuery'@ or @'ParamFormData'@
456+ -- and allows you to send a parameter with a name only or an empty value.
457+ -- Default value is @False@.
458+ , _paramOtherSchemaAllowEmptyValue :: Maybe Bool
481459
482− -- | Parameter 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.
516− } deriving (Eq, Show, Generic, Data, Typeable)
460+ , _paramOtherSchemaParamSchema :: ParamSchema 'SwaggerKindParamOtherSchema
461+ } deriving (Eq, Show, Generic, Typeable, Data)
517462
518463data Example = Example
519464 { -- | Short description for the example.
@@ -537,64 +482,137 @@ data Example = Example
537482 , _exampleExternalValue :: Maybe URL
538483 } deriving (Eq, Show, Generic, Typeable, Data)
539484
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−
576−-- | Items for @'OpenApiArray'@ schemas.
485+-- | Items for @'SwaggerArray'@ schemas.
577486--
578−-- __Warning__: OpenAPI 3.0 does not support tuple arrays. However, OpenAPI 3.1 will, as
579−-- it will incorporate Json Schema mostly verbatim.
487+-- @'SwaggerItemsPrimitive'@ should be used only for query params, headers and path pieces.
488+-- The @'CollectionFormat' t@ parameter specifies how elements of an array should be displayed.
489+-- Note that @fmt@ in @'SwaggerItemsPrimitive' fmt schema@ specifies format for elements of type @schema@.
490+-- This is different from the original Swagger's <http://swagger.io/specification/#itemsObject Items Object>.
580491--
581−-- @'OpenApiItemsObject'@ should be used to specify homogenous array @'Schema'@s.
492+-- @'SwaggerItemsObject'@ should be used to specify homogenous array @'Schema'@s.
582493--
583−-- @'OpenApiItemsArray'@ should be used to specify tuple @'Schema'@s.
584−data OpenApiItems where
585− OpenApiItemsObject :: Referenced Schema -> OpenApiItems
586− OpenApiItemsArray :: [Referenced Schema] -> OpenApiItems
587− deriving (Eq, Show, Typeable, Data)
588−
589−data OpenApiType where
590− OpenApiString :: OpenApiType
591− OpenApiNumber :: OpenApiType
592− OpenApiInteger :: OpenApiType
593− OpenApiBoolean :: OpenApiType
594− OpenApiArray :: OpenApiType
595− OpenApiNull :: OpenApiType
596− OpenApiObject :: OpenApiType
597− deriving (Eq, Show, Typeable, Generic, Data)
494+-- @'SwaggerItemsArray'@ should be used to specify tuple @'Schema'@s.
495+data SwaggerItems t where
496+ SwaggerItemsPrimitive :: Maybe (CollectionFormat k) -> ParamSchema k-> SwaggerItems k
497+ SwaggerItemsObject :: Referenced Schema -> SwaggerItems 'SwaggerKindSchema
498+ SwaggerItemsArray :: [Referenced Schema] -> SwaggerItems 'SwaggerKindSchema
499+ deriving (Typeable)
500+
501+deriving instance Eq (SwaggerItems t)
502+deriving instance Show (SwaggerItems t)
503+--deriving instance Typeable (SwaggerItems t)
504+
505+swaggerItemsPrimitiveConstr :: Constr
506+swaggerItemsPrimitiveConstr = mkConstr swaggerItemsDataType "SwaggerItemsPrimitive" [] Prefix
507+
508+swaggerItemsObjectConstr :: Constr
509+swaggerItemsObjectConstr = mkConstr swaggerItemsDataType "SwaggerItemsObject" [] Prefix
510+
511+swaggerItemsArrayConstr :: Constr
512+swaggerItemsArrayConstr = mkConstr swaggerItemsDataType "SwaggerItemsArray" [] Prefix
513+
514+swaggerItemsDataType :: DataType
515+swaggerItemsDataType = mkDataType "Data.Swagger.SwaggerItems" [swaggerItemsPrimitiveConstr]
516+
517+-- Note: unfortunately we have to write these Data instances by hand,
518+-- to get better contexts / avoid duplicate name when using standalone deriving
519+
520+instance Data t => Data (SwaggerItems ('SwaggerKindNormal t)) where
521+ -- TODO: define gfoldl
522+ gunfold k z c = case constrIndex c of
523+ 1 -> k (k (z SwaggerItemsPrimitive))
524+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems t)."
525+ toConstr _ = swaggerItemsPrimitiveConstr
526+ dataTypeOf _ = swaggerItemsDataType
527+
528+-- SwaggerItems SwaggerKindParamOtherSchema can be constructed using SwaggerItemsPrimitive only
529+instance Data (SwaggerItems 'SwaggerKindParamOtherSchema) where
530+ -- TODO: define gfoldl
531+ gunfold k z c = case constrIndex c of
532+ 1 -> k (k (z SwaggerItemsPrimitive))
533+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems SwaggerKindParamOtherSchema)."
534+ toConstr _ = swaggerItemsPrimitiveConstr
535+ dataTypeOf _ = swaggerItemsDataType
536+
537+instance Data (SwaggerItems 'SwaggerKindSchema) where
538+ gfoldl _ _ (SwaggerItemsPrimitive _ _) = error " Data.Data.gfoldl: Constructor SwaggerItemsPrimitive used to construct SwaggerItems SwaggerKindSchema"
539+ gfoldl k z (SwaggerItemsObject ref) = z SwaggerItemsObject `k` ref
540+ gfoldl k z (SwaggerItemsArray ref) = z SwaggerItemsArray `k` ref
541+
542+ gunfold k z c = case constrIndex c of
543+ 2 -> k (z SwaggerItemsObject)
544+ 3 -> k (z SwaggerItemsArray)
545+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems SwaggerKindSchema)."
546+
547+ toConstr (SwaggerItemsPrimitive _ _) = error "Not supported"
548+ toConstr (SwaggerItemsObject _) = swaggerItemsObjectConstr
549+ toConstr (SwaggerItemsArray _) = swaggerItemsArrayConstr
550+
551+ dataTypeOf _ = swaggerItemsDataType
552+
553+-- | Type used as a kind to avoid overlapping instances.
554+data SwaggerKind t
555+ = SwaggerKindNormal t
556+ | SwaggerKindParamOtherSchema
557+ | SwaggerKindSchema
558+ deriving (Typeable)
559+
560+deriving instance Typeable 'SwaggerKindNormal
561+deriving instance Typeable 'SwaggerKindParamOtherSchema
562+deriving instance Typeable 'SwaggerKindSchema
563+
564+type family SwaggerKindType (k :: SwaggerKind *) :: *
565+type instance SwaggerKindType ('SwaggerKindNormal t) = t
566+type instance SwaggerKindType 'SwaggerKindSchema = Schema
567+type instance SwaggerKindType 'SwaggerKindParamOtherSchema = ParamOtherSchema
568+
569+data SwaggerType t where
570+ SwaggerString :: SwaggerType t
571+ SwaggerNumber :: SwaggerType t
572+ SwaggerInteger :: SwaggerType t
573+ SwaggerBoolean :: SwaggerType t
574+ SwaggerArray :: SwaggerType t
575+ SwaggerFile :: SwaggerType 'SwaggerKindParamOtherSchema
576+ SwaggerNull :: SwaggerType 'SwaggerKindSchema
577+ SwaggerObject :: SwaggerType 'SwaggerKindSchema
578+ deriving (Typeable)
579+
580+deriving instance Eq (SwaggerType t)
581+deriving instance Show (SwaggerType t)
582+
583+swaggerTypeConstr :: Data (SwaggerType t) => SwaggerType t -> Constr
584+swaggerTypeConstr t = mkConstr (dataTypeOf t) (show t) [] Prefix
585+
586+swaggerTypeDataType :: {- Data (SwaggerType t) => -} SwaggerType t -> DataType
587+swaggerTypeDataType _ = mkDataType "Data.Swagger.SwaggerType" swaggerTypeConstrs
588+
589+swaggerCommonTypes :: [SwaggerType k]
590+swaggerCommonTypes = [SwaggerString, SwaggerNumber, SwaggerInteger, SwaggerBoolean, SwaggerArray]
591+
592+swaggerParamTypes :: [SwaggerType 'SwaggerKindParamOtherSchema]
593+swaggerParamTypes = swaggerCommonTypes ++ [SwaggerFile]
594+
595+swaggerSchemaTypes :: [SwaggerType 'SwaggerKindSchema]
596+swaggerSchemaTypes = swaggerCommonTypes ++ [error "SwaggerFile is invalid SwaggerType Schema", SwaggerNull, SwaggerObject]
597+
598+swaggerTypeConstrs :: [Constr]
599+swaggerTypeConstrs = map swaggerTypeConstr (swaggerCommonTypes :: [SwaggerType 'SwaggerKindSchema])
600+ ++ [swaggerTypeConstr SwaggerFile, swaggerTypeConstr SwaggerNull, swaggerTypeConstr SwaggerObject]
601+
602+instance Typeable t => Data (SwaggerType ('SwaggerKindNormal t)) where
603+ gunfold = gunfoldEnum "SwaggerType" swaggerCommonTypes
604+ toConstr = swaggerTypeConstr
605+ dataTypeOf = swaggerTypeDataType
606+
607+instance Data (SwaggerType 'SwaggerKindParamOtherSchema) where
608+ gunfold = gunfoldEnum "SwaggerType ParamOtherSchema" swaggerParamTypes
609+ toConstr = swaggerTypeConstr
610+ dataTypeOf = swaggerTypeDataType
611+
612+instance Data (SwaggerType 'SwaggerKindSchema) where
613+ gunfold = gunfoldEnum "SwaggerType Schema" swaggerSchemaTypes
614+ toConstr = swaggerTypeConstr
615+ dataTypeOf = swaggerTypeDataType
598616
599617data ParamLocation
600618 = -- | Parameters that are appended to the URL.
@@ -612,6 +630,42 @@ data ParamLocation
612630
613631type Format = Text
614632
633+-- | Determines the format of the array.
634+data CollectionFormat t where
635+ -- Comma separated values: @foo,bar@.
636+ CollectionCSV :: CollectionFormat t
637+ -- Space separated values: @foo bar@.
638+ CollectionSSV :: CollectionFormat t
639+ -- Tab separated values: @foo\\tbar@.
640+ CollectionTSV :: CollectionFormat t
641+ -- Pipe separated values: @foo|bar@.
642+ CollectionPipes :: CollectionFormat t
643+ -- Corresponds to multiple parameter instances
644+ -- instead of multiple values for a single instance @foo=bar&foo=baz@.
645+ -- This is valid only for parameters in @'ParamQuery'@ or @'ParamFormData'@.
646+ CollectionMulti :: CollectionFormat 'SwaggerKindParamOtherSchema
647+ deriving (Typeable)
648+
649+deriving instance Eq (CollectionFormat t)
650+deriving instance Show (CollectionFormat t)
651+
652+collectionFormatConstr :: CollectionFormat t -> Constr
653+collectionFormatConstr cf = mkConstr collectionFormatDataType (show cf) [] Prefix
654+
655+collectionFormatDataType :: DataType
656+collectionFormatDataType = mkDataType "Data.Swagger.CollectionFormat" $
657+ map collectionFormatConstr collectionCommonFormats
658+
659+collectionCommonFormats :: [CollectionFormat t]
660+collectionCommonFormats = [ CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes ]
661+
662+instance Data t => Data (CollectionFormat ('SwaggerKindNormal t)) where
663+ gunfold = gunfoldEnum "CollectionFormat" collectionCommonFormats
664+ toConstr = collectionFormatConstr
665+ dataTypeOf _ = collectionFormatDataType
666+
667+deriving instance Data (CollectionFormat 'SwaggerKindParamOtherSchema)
668+
615669type ParamName = Text
616670
617671data Schema = Schema
@@ -638,33 +692,9 @@ data Schema = Schema
638692 , _schemaMaxProperties :: Maybe Integer
639693 , _schemaMinProperties :: Maybe Integer
640694
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
695+ , _schemaParamSchema :: ParamSchema 'SwaggerKindSchema
663696 } deriving (Eq, Show, Generic, Data, Typeable)
664697
665−-- | Regex pattern for @string@ type.
666−type Pattern = Text
667−
668698data Discriminator = Discriminator
669699 { -- | The name of the property in the payload that will hold the discriminator value.
670700 _discriminatorPropertyName :: Text
@@ -680,9 +710,41 @@ data NamedSchema = NamedSchema
680710 , _namedSchemaSchema :: Schema
681711 } deriving (Eq, Show, Generic, Data, Typeable)
682712
713+-- | Regex pattern for @string@ type.
714+type Pattern = Text
715+
716+-- TODO examples for params
717+
718+data ParamSchema (t :: SwaggerKind *) = ParamSchema
719+ { -- | Declares the value of the parameter that the server will use if none is provided,
720+ -- for example a @"count"@ to control the number of results per page might default to @100@
721+ -- if not supplied by the client in the request.
722+ -- (Note: "default" has no meaning for required parameters.)
723+ -- Unlike JSON Schema this value MUST conform to the defined type for this parameter.
724+ _paramSchemaDefault :: Maybe Value
725+
726+ , _paramSchemaType :: Maybe (SwaggerType t)
727+ , _paramSchemaFormat :: Maybe Format
728+ , _paramSchemaItems :: Maybe (SwaggerItems t)
729+ , _paramSchemaMaximum :: Maybe Scientific
730+ , _paramSchemaExclusiveMaximum :: Maybe Bool
731+ , _paramSchemaMinimum :: Maybe Scientific
732+ , _paramSchemaExclusiveMinimum :: Maybe Bool
733+ , _paramSchemaMaxLength :: Maybe Integer
734+ , _paramSchemaMinLength :: Maybe Integer
735+ , _paramSchemaPattern :: Maybe Pattern
736+ , _paramSchemaMaxItems :: Maybe Integer
737+ , _paramSchemaMinItems :: Maybe Integer
738+ , _paramSchemaUniqueItems :: Maybe Bool
739+ , _paramSchemaEnum :: Maybe [Value]
740+ , _paramSchemaMultipleOf :: Maybe Scientific
741+ } deriving (Eq, Show, Generic, Typeable)
742+
743+deriving instance (Typeable k, Data (Maybe (SwaggerType k)), Data (SwaggerItems k)) => Data (ParamSchema k)
744+
683745data Xml = Xml
684746 { -- | Replaces the name of the element/attribute used for the described schema property.
685− -- When defined within the @'OpenApiItems'@ (items), it will affect the name of the individual XML elements within the list.
747+ -- When defined within the @'SwaggerItems'@ (items), it will affect the name of the individual XML elements within the list.
686748 -- When defined alongside type being array (outside the items),
687749 -- it will affect the wrapping element and only if wrapped is true.
688750 -- If wrapped is false, it will be ignored.
@@ -734,46 +796,25 @@ data Response = Response
734796 -- | A map containing descriptions of potential response payloads.
735797 -- The key is a media type or media type range and the value describes it.
736798 -- For responses that match multiple keys, only the most specific key is applicable.
737− -- e.g. @text/plain@ overrides @text/\*@.
738− , _responseContent :: InsOrdHashMap MediaType MediaTypeObject
799+ -- e.g. @text/plain@ overrides @text/*@.
800+ , _responseContent :: InsOrdHashMap Text MediaTypeObject
739801
740802 -- | Maps a header name to its definition.
741803 , _responseHeaders :: InsOrdHashMap HeaderName (Referenced Header)
742804
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)
805+ -- TODO links
747806 } deriving (Eq, Show, Generic, Data, Typeable)
748807
749808instance IsString Response where
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)
809+ fromString s = Response (fromString s) mempty mempty
759810
760811type HeaderName = Text
761812
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.
765813data Header = Header
766814 { -- | A short description of the header.
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)
815+ _headerDescription :: Maybe Text
775816
776− , _headerSchema :: Maybe (Referenced Schema)
817+ , _headerParamSchema :: ParamSchema ('SwaggerKindNormal Header)
777818 } deriving (Eq, Show, Generic, Data, Typeable)
778819
779820-- | The location of the API key.
@@ -797,96 +838,26 @@ type AuthorizationURL = Text
797838-- | The token URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
798839type TokenURL = Text
799840
800−newtype OAuth2ImplicitFlow
801− = OAuth2ImplicitFlow {_oAuth2ImplicitFlowAuthorizationUrl :: AuthorizationURL}
802− deriving (Eq, Show, Generic, Data, Typeable)
803−
804−newtype OAuth2PasswordFlow
805− = OAuth2PasswordFlow {_oAuth2PasswordFlowTokenUrl :: TokenURL}
806− deriving (Eq, Show, Generic, Data, Typeable)
807−
808−newtype OAuth2ClientCredentialsFlow
809− = OAuth2ClientCredentialsFlow {_oAuth2ClientCredentialsFlowTokenUrl :: TokenURL}
841+data OAuth2Flow
842+ = OAuth2Implicit AuthorizationURL
843+ | OAuth2Password TokenURL
844+ | OAuth2Application TokenURL
845+ | OAuth2AccessCode AuthorizationURL TokenURL
810846 deriving (Eq, Show, Generic, Data, Typeable)
811847
812−data OAuth2AuthorizationCodeFlow = OAuth2AuthorizationCodeFlow
813− { _oAuth2AuthorizationCodeFlowAuthorizationUrl :: AuthorizationURL
814− , _oAuth2AuthorizationCodeFlowTokenUrl :: TokenURL
815− } deriving (Eq, Show, Generic, Data, Typeable)
816−
817−data OAuth2Flow p = OAuth2Flow
818− { _oAuth2Params :: p
819−
820− -- | The URL to be used for obtaining refresh tokens.
821− , _oAath2RefreshUrl :: Maybe URL
848+data OAuth2Params = OAuth2Params
849+ { -- | The flow used by the OAuth2 security scheme.
850+ _oauth2Flow :: OAuth2Flow
822851
823852 -- | The available scopes for the OAuth2 security scheme.
824− -- A map between the scope name and a short description for it.
825− -- The map MAY be empty.
826− , _oAuth2Scopes :: InsOrdHashMap Text Text
827− } deriving (Eq, Show, Generic, Data, Typeable)
828−
829−data OAuth2Flows = OAuth2Flows
830− { -- | Configuration for the OAuth Implicit flow
831− _oAuth2FlowsImplicit :: Maybe (OAuth2Flow OAuth2ImplicitFlow)
832−
833− -- | Configuration for the OAuth Resource Owner Password flow
834− , _oAuth2FlowsPassword :: Maybe (OAuth2Flow OAuth2PasswordFlow)
835−
836− -- | Configuration for the OAuth Client Credentials flow
837− , _oAuth2FlowsClientCredentials :: Maybe (OAuth2Flow OAuth2ClientCredentialsFlow)
838−
839− -- | Configuration for the OAuth Authorization Code flow
840− , _oAuth2FlowsAuthorizationCode :: Maybe (OAuth2Flow OAuth2AuthorizationCodeFlow)
853+ , _oauth2Scopes :: InsOrdHashMap Text Text
841854 } deriving (Eq, Show, Generic, Data, Typeable)
842855
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−--
885856data SecuritySchemeType
886− = SecuritySchemeHttp HttpSchemeType
857+ = SecuritySchemeHttp
887858 | SecuritySchemeApiKey ApiKeyParams
888− | SecuritySchemeOAuth2 OAuth2Flows
889− | SecuritySchemeOpenIdConnect URL
859+ | SecuritySchemeOAuth2 OAuth2Params
860+-- | SecuritySchemeOpenIdConnect -- FIXME
890861 deriving (Eq, Show, Generic, Data, Typeable)
891862
892863data SecurityScheme = SecurityScheme
@@ -897,6 +868,18 @@ data SecurityScheme = SecurityScheme
897868 , _securitySchemeDescription :: Maybe Text
898869 } deriving (Eq, Show, Generic, Data, Typeable)
899870
871+
872+-- | merge scopes of two OAuth2 security schemes when their flows are identical.
873+-- In other case returns first security scheme
874+mergeSecurityScheme :: SecurityScheme -> SecurityScheme -> SecurityScheme
875+mergeSecurityScheme s1@(SecurityScheme (SecuritySchemeOAuth2 (OAuth2Params flow1 scopes1)) desc)
876+ s2@(SecurityScheme (SecuritySchemeOAuth2 (OAuth2Params flow2 scopes2)) _)
877+ = if flow1 == flow2 then
878+ SecurityScheme (SecuritySchemeOAuth2 (OAuth2Params flow1 (scopes1 <> scopes2))) desc
879+ else
880+ s1
881+mergeSecurityScheme s1 _ = s1
882+
900883newtype SecurityDefinitions
901884 = SecurityDefinitions (Definitions SecurityScheme)
902885 deriving (Eq, Show, Generic, Data, Typeable)
@@ -918,7 +901,7 @@ data Tag = Tag
918901 _tagName :: TagName
919902
920903 -- | A short description for the tag.
921− -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
904+ -- GFM syntax can be used for rich text representation.
922905 , _tagDescription :: Maybe Text
923906
924907 -- | Additional external documentation for this tag.
@@ -933,7 +916,7 @@ instance IsString Tag where
933916-- | Allows referencing an external resource for extended documentation.
934917data ExternalDocs = ExternalDocs
935918 { -- | A short description of the target documentation.
936− -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
919+ -- GFM syntax can be used for rich text representation.
937920 _externalDocsDescription :: Maybe Text
938921
939922 -- | The URL for the target documentation.
@@ -966,13 +949,12 @@ data AdditionalProperties
966949-- Generic instances
967950-------------------------------------------------------------------------------
968951
969−deriveGeneric ''Server
970952deriveGeneric ''Components
971953deriveGeneric ''Header
972−deriveGeneric ''OAuth2Flow
973−deriveGeneric ''OAuth2Flows
954+deriveGeneric ''OAuth2Params
974955deriveGeneric ''Operation
975956deriveGeneric ''Param
957+deriveGeneric ''ParamOtherSchema
976958deriveGeneric ''PathItem
977959deriveGeneric ''Response
978960deriveGeneric ''RequestBody
@@ -980,18 +962,18 @@ deriveGeneric ''MediaTypeObject
980962deriveGeneric ''Responses
981963deriveGeneric ''SecurityScheme
982964deriveGeneric ''Schema
983−deriveGeneric ''OpenApi
965+deriveGeneric ''ParamSchema
966+deriveGeneric ''Swagger
984967deriveGeneric ''Example
985968deriveGeneric ''Encoding
986−deriveGeneric ''Link
987969
988970-- =======================================================================
989971-- Monoid instances
990972-- =======================================================================
991973
992−instance Semigroup OpenApi where
974+instance Semigroup Swagger where
993975 (<>) = genericMappend
994−instance Monoid OpenApi where
976+instance Monoid Swagger where
995977 mempty = genericMempty
996978 mappend = (<>)
997979
@@ -1025,12 +1007,24 @@ instance Monoid Schema where
10251007 mempty = genericMempty
10261008 mappend = (<>)
10271009
1010+instance Semigroup (ParamSchema t) where
1011+ (<>) = genericMappend
1012+instance Monoid (ParamSchema t) where
1013+ mempty = genericMempty
1014+ mappend = (<>)
1015+
10281016instance Semigroup Param where
10291017 (<>) = genericMappend
10301018instance Monoid Param where
10311019 mempty = genericMempty
10321020 mappend = (<>)
10331021
1022+instance Semigroup ParamOtherSchema where
1023+ (<>) = genericMappend
1024+instance Monoid ParamOtherSchema where
1025+ mempty = genericMempty
1026+ mappend = (<>)
1027+
10341028instance Semigroup Header where
10351029 (<>) = genericMappend
10361030instance Monoid Header where
@@ -1073,29 +1067,8 @@ instance Monoid Operation where
10731067 mempty = genericMempty
10741068 mappend = (<>)
10751069
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.
1082−instance Semigroup OAuth2Flows where
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−
1090−instance Monoid OAuth2Flows where
1091− mempty = genericMempty
1092− mappend = (<>)
1093−
10941070instance Semigroup SecurityScheme where
1095− SecurityScheme (SecuritySchemeOAuth2 lFlows) lDesc
1096− <> SecurityScheme (SecuritySchemeOAuth2 rFlows) rDesc =
1097− SecurityScheme (SecuritySchemeOAuth2 $ lFlows <> rFlows) (swaggerMappend lDesc rDesc)
1098− l <> _ = l
1071+ (<>) = mergeSecurityScheme
10991072
11001073instance Semigroup SecurityDefinitions where
11011074 (SecurityDefinitions sd1) <> (SecurityDefinitions sd2) =
@@ -1105,12 +1078,6 @@ instance Monoid SecurityDefinitions where
11051078 mempty = SecurityDefinitions InsOrdHashMap.empty
11061079 mappend = (<>)
11071080
1108−instance Semigroup RequestBody where
1109− (<>) = genericMappend
1110−instance Monoid RequestBody where
1111− mempty = genericMempty
1112− mappend = (<>)
1113−
11141081-- =======================================================================
11151082-- SwaggerMonoid helper instances
11161083-- =======================================================================
@@ -1119,19 +1086,21 @@ instance SwaggerMonoid Info
11191086instance SwaggerMonoid Components
11201087instance SwaggerMonoid PathItem
11211088instance SwaggerMonoid Schema
1089+instance SwaggerMonoid (ParamSchema t)
11221090instance SwaggerMonoid Param
1091+instance SwaggerMonoid ParamOtherSchema
11231092instance SwaggerMonoid Responses
11241093instance SwaggerMonoid Response
11251094instance SwaggerMonoid ExternalDocs
11261095instance SwaggerMonoid Operation
1127−instance (Eq a, Hashable a) => SwaggerMonoid (InsOrdHashSet a)
11281096instance SwaggerMonoid SecurityDefinitions
1097+instance (Eq a, Hashable a) => SwaggerMonoid (InsOrdHashSet a)
11291098
11301099instance SwaggerMonoid MimeList
11311100deriving instance SwaggerMonoid URL
11321101
1133−instance SwaggerMonoid OpenApiType where
1134− swaggerMempty = OpenApiString
1102+instance SwaggerMonoid (SwaggerType t) where
1103+ swaggerMempty = SwaggerString
11351104 swaggerMappend _ y = y
11361105
11371106instance SwaggerMonoid ParamLocation where
@@ -1147,16 +1116,16 @@ instance Monoid a => SwaggerMonoid (Referenced a) where
11471116 swaggerMappend (Inline x) (Inline y) = Inline (mappend x y)
11481117 swaggerMappend _ y = y
11491118
1119+instance SwaggerMonoid ParamAnySchema where
1120+ swaggerMempty = ParamOther swaggerMempty
1121+ swaggerMappend (ParamBody x) (ParamBody y) = ParamBody (swaggerMappend x y)
1122+ swaggerMappend (ParamOther x) (ParamOther y) = ParamOther (swaggerMappend x y)
1123+ swaggerMappend _ y = y
1124+
11501125-- =======================================================================
11511126-- Simple Generic-based ToJSON instances
11521127-- =======================================================================
11531128
1154−instance ToJSON Style where
1155− toJSON = genericToJSON (jsonPrefix "Style")
1156−
1157−instance ToJSON OpenApiType where
1158− toJSON = genericToJSON (jsonPrefix "Swagger")
1159−
11601129instance ToJSON ParamLocation where
11611130 toJSON = genericToJSON (jsonPrefix "Param")
11621131
@@ -1169,6 +1138,9 @@ instance ToJSON Contact where
11691138instance ToJSON License where
11701139 toJSON = genericToJSON (jsonPrefix "License")
11711140
1141+instance ToJSON Server where
1142+ toJSON = genericToJSON (jsonPrefix "Server")
1143+
11721144instance ToJSON ServerVariable where
11731145 toJSON = genericToJSON (jsonPrefix "ServerVariable")
11741146
@@ -1178,6 +1150,9 @@ instance ToJSON ApiKeyLocation where
11781150instance ToJSON ApiKeyParams where
11791151 toJSON = genericToJSON (jsonPrefix "apiKey")
11801152
1153+instance ToJSON Scheme where
1154+ toJSON = genericToJSON (jsonPrefix "")
1155+
11811156instance ToJSON Tag where
11821157 toJSON = genericToJSON (jsonPrefix "Tag")
11831158
@@ -1190,28 +1165,10 @@ instance ToJSON Xml where
11901165instance ToJSON Discriminator where
11911166 toJSON = genericToJSON (jsonPrefix "Discriminator")
11921167
1193−instance ToJSON OAuth2ImplicitFlow where
1194− toJSON = genericToJSON (jsonPrefix "OAuth2ImplicitFlow")
1195−
1196−instance ToJSON OAuth2PasswordFlow where
1197− toJSON = genericToJSON (jsonPrefix "OAuth2PasswordFlow")
1198−
1199−instance ToJSON OAuth2ClientCredentialsFlow where
1200− toJSON = genericToJSON (jsonPrefix "OAuth2ClientCredentialsFlow")
1201−
1202−instance ToJSON OAuth2AuthorizationCodeFlow where
1203− toJSON = genericToJSON (jsonPrefix "OAuth2AuthorizationCodeFlow")
1204−
12051168-- =======================================================================
12061169-- Simple Generic-based FromJSON instances
12071170-- =======================================================================
12081171
1209−instance FromJSON Style where
1210− parseJSON = genericParseJSON (jsonPrefix "Style")
1211−
1212−instance FromJSON OpenApiType where
1213− parseJSON = genericParseJSON (jsonPrefix "Swagger")
1214−
12151172instance FromJSON ParamLocation where
12161173 parseJSON = genericParseJSON (jsonPrefix "Param")
12171174
@@ -1224,6 +1181,9 @@ instance FromJSON Contact where
12241181instance FromJSON License where
12251182 parseJSON = genericParseJSON (jsonPrefix "License")
12261183
1184+instance FromJSON Server where
1185+ parseJSON = genericParseJSON (jsonPrefix "Server")
1186+
12271187instance FromJSON ServerVariable where
12281188 parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
12291189
@@ -1233,6 +1193,9 @@ instance FromJSON ApiKeyLocation where
12331193instance FromJSON ApiKeyParams where
12341194 parseJSON = genericParseJSON (jsonPrefix "apiKey")
12351195
1196+instance FromJSON Scheme where
1197+ parseJSON = genericParseJSON (jsonPrefix "")
1198+
12361199instance FromJSON Tag where
12371200 parseJSON = genericParseJSON (jsonPrefix "Tag")
12381201
@@ -1242,81 +1205,53 @@ instance FromJSON ExternalDocs where
12421205instance FromJSON Discriminator where
12431206 parseJSON = genericParseJSON (jsonPrefix "Discriminator")
12441207
1245−instance FromJSON OAuth2ImplicitFlow where
1246− parseJSON = genericParseJSON (jsonPrefix "OAuth2ImplicitFlow")
1247−
1248−instance FromJSON OAuth2PasswordFlow where
1249− parseJSON = genericParseJSON (jsonPrefix "OAuth2PasswordFlow")
1250−
1251−instance FromJSON OAuth2ClientCredentialsFlow where
1252− parseJSON = genericParseJSON (jsonPrefix "OAuth2ClientCredentialsFlow")
1253−
1254−instance FromJSON OAuth2AuthorizationCodeFlow where
1255− parseJSON = genericParseJSON (jsonPrefix "OAuth2AuthorizationCodeFlow")
1256−
12571208-- =======================================================================
12581209-- Manual ToJSON instances
12591210-- =======================================================================
12601211
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−
1268−instance (Eq p, ToJSON p, AesonDefaultValue p) => ToJSON (OAuth2Flow p) where
1269− toJSON = sopSwaggerGenericToJSON
1270− toEncoding = sopSwaggerGenericToEncoding
1271−
1272−instance ToJSON OAuth2Flows where
1212+instance ToJSON OAuth2Flow where
1213+ toJSON (OAuth2Implicit authUrl) = object
1214+ [ "flow" .= ("implicit" :: Text)
1215+ , "authorizationUrl" .= authUrl ]
1216+ toJSON (OAuth2Password tokenUrl) = object
1217+ [ "flow" .= ("password" :: Text)
1218+ , "tokenUrl" .= tokenUrl ]
1219+ toJSON (OAuth2Application tokenUrl) = object
1220+ [ "flow" .= ("application" :: Text)
1221+ , "tokenUrl" .= tokenUrl ]
1222+ toJSON (OAuth2AccessCode authUrl tokenUrl) = object
1223+ [ "flow" .= ("accessCode" :: Text)
1224+ , "authorizationUrl" .= authUrl
1225+ , "tokenUrl" .= tokenUrl ]
1226+
1227+instance ToJSON OAuth2Params where
12731228 toJSON = sopSwaggerGenericToJSON
12741229 toEncoding = sopSwaggerGenericToEncoding
12751230
12761231instance ToJSON SecuritySchemeType where
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 ->
1287− object [ "type" .= ("http" :: Text)
1288− , "scheme" .= t
1289− ]
1232+ toJSON SecuritySchemeHttp
1233+ = object [ "type" .= ("http" :: Text) ]
12901234 toJSON (SecuritySchemeApiKey params)
12911235 = toJSON params
12921236 <+> object [ "type" .= ("apiKey" :: Text) ]
1293− toJSON (SecuritySchemeOAuth2 params) = object
1294− [ "type" .= ("oauth2" :: Text)
1295− , "flows" .= toJSON params
1296− ]
1297− toJSON (SecuritySchemeOpenIdConnect url) = object
1298− [ "type" .= ("openIdConnect" :: Text)
1299− , "openIdConnectUrl" .= url
1300− ]
1237+ toJSON (SecuritySchemeOAuth2 params)
1238+ = toJSON params
1239+ <+> object [ "type" .= ("oauth2" :: Text) ]
13011240
1302−instance ToJSON OpenApi where
1241+instance ToJSON Swagger where
13031242 toJSON a = sopSwaggerGenericToJSON a &
1304− if InsOrdHashMap.null (_openApiPaths a)
1243+ if InsOrdHashMap.null (_swaggerPaths a)
13051244 then (<+> object ["paths" .= object []])
13061245 else id
13071246 toEncoding = sopSwaggerGenericToEncoding
13081247
1309−instance ToJSON Server where
1310− toJSON = sopSwaggerGenericToJSON
1311− toEncoding = sopSwaggerGenericToEncoding
1312−
13131248instance ToJSON SecurityScheme where
13141249 toJSON = sopSwaggerGenericToJSON
13151250 toEncoding = sopSwaggerGenericToEncoding
13161251
13171252instance ToJSON Schema where
1318− toJSON = sopSwaggerGenericToJSONWithOpts $
1319− mkSwaggerAesonOptions "schema" & saoSubObject ?~ "items"
1253+ toJSON = sopSwaggerGenericToJSON
1254+ toEncoding = sopSwaggerGenericToEncoding
13201255
13211256instance ToJSON Header where
13221257 toJSON = sopSwaggerGenericToJSON
@@ -1325,26 +1260,31 @@ instance ToJSON Header where
13251260-- | As for nullary schema for 0-arity type constructors, see
13261261-- <https://github.com/GetShopTV/swagger2/issues/167>.
13271262--
1328−-- >>> BSL.putStrLn $ encodePretty (OpenApiItemsArray [])
1329−-- {
1330−-- "example": [],
1331−-- "items": {},
1332−-- "maxItems": 0
1333−-- }
1263+-- >>> encode (SwaggerItemsArray [])
1264+-- "{\"example\":[],\"items\":{},\"maxItems\":0}"
13341265--
1335−instance ToJSON OpenApiItems where
1336− toJSON (OpenApiItemsObject x) = object [ "items" .= x ]
1337− toJSON (OpenApiItemsArray []) = object
1266+instance ToJSON (ParamSchema t) => ToJSON (SwaggerItems t) where
1267+ toJSON (SwaggerItemsPrimitive fmt schema) = object
1268+ [ "collectionFormat" .= fmt
1269+ , "items" .= schema ]
1270+ toJSON (SwaggerItemsObject x) = object [ "items" .= x ]
1271+ toJSON (SwaggerItemsArray []) = object
13381272 [ "items" .= object []
13391273 , "maxItems" .= (0 :: Int)
13401274 , "example" .= Array mempty
13411275 ]
1342− toJSON (OpenApiItemsArray x) = object [ "items" .= x ]
1276+ toJSON (SwaggerItemsArray x) = object [ "items" .= x ]
13431277
13441278instance ToJSON Components where
13451279 toJSON = sopSwaggerGenericToJSON
13461280 toEncoding = sopSwaggerGenericToEncoding
13471281
1282+instance ToJSON Host where
1283+ toJSON (Host host mport) = toJSON $
1284+ case mport of
1285+ Nothing -> host
1286+ Just port -> host ++ ":" ++ show port
1287+
13481288instance ToJSON MimeList where
13491289 toJSON (MimeList xs) = toJSON (map show xs)
13501290
@@ -1352,6 +1292,14 @@ instance ToJSON Param where
13521292 toJSON = sopSwaggerGenericToJSON
13531293 toEncoding = sopSwaggerGenericToEncoding
13541294
1295+instance ToJSON ParamAnySchema where
1296+ toJSON (ParamBody s) = object [ "in" .= ("body" :: Text), "schema" .= s ]
1297+ toJSON (ParamOther s) = toJSON s
1298+
1299+instance ToJSON ParamOtherSchema where
1300+ toJSON = sopSwaggerGenericToJSON
1301+ toEncoding = sopSwaggerGenericToEncoding
1302+
13551303instance ToJSON Responses where
13561304 toJSON = sopSwaggerGenericToJSON
13571305 toEncoding = sopSwaggerGenericToEncoding
@@ -1384,10 +1332,6 @@ instance ToJSON Encoding where
13841332 toJSON = sopSwaggerGenericToJSON
13851333 toEncoding = sopSwaggerGenericToEncoding
13861334
1387−instance ToJSON Link where
1388− toJSON = sopSwaggerGenericToJSON
1389− toEncoding = sopSwaggerGenericToEncoding
1390−
13911335instance ToJSON SecurityDefinitions where
13921336 toJSON (SecurityDefinitions sd) = toJSON sd
13931337
@@ -1404,57 +1348,64 @@ instance ToJSON (Referenced Response) where toJSON = referencedToJSON "#/compone
14041348instance ToJSON (Referenced RequestBody) where toJSON = referencedToJSON "#/components/requestBodies/"
14051349instance ToJSON (Referenced Example) where toJSON = referencedToJSON "#/components/examples/"
14061350instance ToJSON (Referenced Header) where toJSON = referencedToJSON "#/components/headers/"
1407−instance ToJSON (Referenced Link) where toJSON = referencedToJSON "#/components/links/"
1408−instance ToJSON (Referenced Callback) where toJSON = referencedToJSON "#/components/callbacks/"
1351+
1352+instance ToJSON (SwaggerType t) where
1353+ toJSON SwaggerArray = "array"
1354+ toJSON SwaggerString = "string"
1355+ toJSON SwaggerInteger = "integer"
1356+ toJSON SwaggerNumber = "number"
1357+ toJSON SwaggerBoolean = "boolean"
1358+ toJSON SwaggerFile = "file"
1359+ toJSON SwaggerNull = "null"
1360+ toJSON SwaggerObject = "object"
1361+
1362+instance ToJSON (CollectionFormat t) where
1363+ toJSON CollectionCSV = "csv"
1364+ toJSON CollectionSSV = "ssv"
1365+ toJSON CollectionTSV = "tsv"
1366+ toJSON CollectionPipes = "pipes"
1367+ toJSON CollectionMulti = "multi"
1368+
1369+instance ToJSON (ParamSchema k) where
1370+ -- TODO: this is a bit fishy, why we need sub object only in `ToJSON`?
1371+ toJSON = sopSwaggerGenericToJSONWithOpts $
1372+ mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
14091373
14101374instance ToJSON AdditionalProperties where
14111375 toJSON (AdditionalPropertiesAllowed b) = toJSON b
14121376 toJSON (AdditionalPropertiesSchema s) = toJSON s
14131377
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−
14211378-- =======================================================================
14221379-- Manual FromJSON instances
14231380-- =======================================================================
14241381
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−
1432−instance (Eq p, FromJSON p, AesonDefaultValue p) => FromJSON (OAuth2Flow p) where
1433− parseJSON = sopSwaggerGenericParseJSON
1382+instance FromJSON OAuth2Flow where
1383+ parseJSON (Object o) = do
1384+ (flow :: Text) <- o .: "flow"
1385+ case flow of
1386+ "implicit" -> OAuth2Implicit <$> o .: "authorizationUrl"
1387+ "password" -> OAuth2Password <$> o .: "tokenUrl"
1388+ "application" -> OAuth2Application <$> o .: "tokenUrl"
1389+ "accessCode" -> OAuth2AccessCode
1390+ <$> o .: "authorizationUrl"
1391+ <*> o .: "tokenUrl"
1392+ _ -> empty
1393+ parseJSON _ = empty
14341394
1435−instance FromJSON OAuth2Flows where
1395+instance FromJSON OAuth2Params where
14361396 parseJSON = sopSwaggerGenericParseJSON
14371397
14381398instance FromJSON SecuritySchemeType where
14391399 parseJSON js@(Object o) = do
14401400 (t :: Text) <- o .: "type"
14411401 case t of
1442− "http" -> do
1443− scheme <- o .: "scheme"
1444− SecuritySchemeHttp <$> case scheme of
1445− "bearer" -> HttpSchemeBearer <$> (o .:! "bearerFormat")
1446− "basic" -> pure HttpSchemeBasic
1447− t -> pure $ HttpSchemeCustom t
1402+ "http" -> pure SecuritySchemeHttp
14481403 "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1449− "oauth2" -> SecuritySchemeOAuth2 <$> (o .: "flows")
1450− "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
1404+ "oauth2" -> SecuritySchemeOAuth2 <$> parseJSON js
14511405 _ -> empty
14521406 parseJSON _ = empty
14531407
1454−instance FromJSON OpenApi where
1455− parseJSON = sopSwaggerGenericParseJSON
1456−
1457−instance FromJSON Server where
1408+instance FromJSON Swagger where
14581409 parseJSON = sopSwaggerGenericParseJSON
14591410
14601411instance FromJSON SecurityScheme where
@@ -1463,36 +1414,78 @@ instance FromJSON SecurityScheme where
14631414instance FromJSON Schema where
14641415 parseJSON = fmap nullaryCleanup . sopSwaggerGenericParseJSON
14651416 where nullaryCleanup :: Schema -> Schema
1466− nullaryCleanup s =
1467− if _schemaItems s == Just (OpenApiItemsArray [])
1417+ nullaryCleanup s@Schema{_schemaParamSchema=ps} =
1418+ if _paramSchemaItems ps == Just (SwaggerItemsArray [])
14681419 then s { _schemaExample = Nothing
1469− , _schemaMaxItems = Nothing
1470− }
1420+ , _schemaParamSchema = ps { _paramSchemaMaxItems = Nothing } }
14711421 else s
14721422
14731423instance FromJSON Header where
14741424 parseJSON = sopSwaggerGenericParseJSON
14751425
1476−instance FromJSON OpenApiItems where
1426+instance (FromJSON (CollectionFormat ('SwaggerKindNormal t)), FromJSON (ParamSchema ('SwaggerKindNormal t))) => FromJSON (SwaggerItems ('SwaggerKindNormal t)) where
1427+ parseJSON = withObject "SwaggerItemsPrimitive" $ \o -> SwaggerItemsPrimitive
1428+ <$> o .:? "collectionFormat"
1429+ <*> (o .: "items" >>= parseJSON)
1430+
1431+instance FromJSON (SwaggerItems 'SwaggerKindParamOtherSchema) where
1432+ parseJSON = withObject "SwaggerItemsPrimitive" $ \o -> SwaggerItemsPrimitive
1433+ <$> o .:? "collectionFormat"
1434+ <*> ((o .: "items" >>= parseJSON) <|> fail ("foo" ++ show o))
1435+
1436+-- |
1437+--
1438+-- >>> decode "{}" :: Maybe (SwaggerItems 'SwaggerKindSchema)
1439+-- Just (SwaggerItemsArray [])
1440+--
1441+-- >>> eitherDecode "{\"$ref\":\"#/definitions/example\"}" :: Either String (SwaggerItems 'SwaggerKindSchema)
1442+-- Right (SwaggerItemsObject (Ref (Reference {getReference = "example"})))
1443+--
1444+-- >>> eitherDecode "[{\"$ref\":\"#/definitions/example\"}]" :: Either String (SwaggerItems 'SwaggerKindSchema)
1445+-- Right (SwaggerItemsArray [Ref (Reference {getReference = "example"})])
1446+--
1447+instance FromJSON (SwaggerItems 'SwaggerKindSchema) where
14771448 parseJSON js@(Object obj)
1478− | null obj = pure $ OpenApiItemsArray [] -- Nullary schema.
1479− | otherwise = OpenApiItemsObject <$> parseJSON js
1480− parseJSON js@(Array _) = OpenApiItemsArray <$> parseJSON js
1449+ | null obj = pure $ SwaggerItemsArray [] -- Nullary schema.
1450+ | otherwise = SwaggerItemsObject <$> parseJSON js
1451+ parseJSON js@(Array _) = SwaggerItemsArray <$> parseJSON js
14811452 parseJSON _ = empty
14821453
14831454instance FromJSON Components where
14841455 parseJSON = sopSwaggerGenericParseJSON
14851456
1457+instance FromJSON Host where
1458+ parseJSON (String s) = case map Text.unpack $ Text.split (== ':') s of
1459+ [host] -> return $ Host host Nothing
1460+ [host, port] -> case readMaybe port of
1461+ Nothing -> fail $ "Invalid port `" ++ port ++ "'"
1462+ Just p -> return $ Host host (Just (fromInteger p))
1463+ _ -> fail $ "Invalid host `" ++ Text.unpack s ++ "'"
1464+ parseJSON _ = empty
1465+
14861466instance FromJSON MimeList where
1487− parseJSON js = MimeList . map fromString <$> parseJSON js
1467+ parseJSON js = (MimeList . map fromString) <$> parseJSON js
14881468
14891469instance FromJSON Param where
14901470 parseJSON = sopSwaggerGenericParseJSON
14911471
1472+instance FromJSON ParamAnySchema where
1473+ parseJSON js@(Object o) = do
1474+ (i :: Text) <- o .: "in"
1475+ case i of
1476+ "body" -> do
1477+ schema <- o .: "schema"
1478+ ParamBody <$> parseJSON schema
1479+ _ -> ParamOther <$> parseJSON js
1480+ parseJSON _ = empty
1481+
1482+instance FromJSON ParamOtherSchema where
1483+ parseJSON = sopSwaggerGenericParseJSON
1484+
14921485instance FromJSON Responses where
14931486 parseJSON (Object o) = Responses
14941487 <$> o .:? "default"
1495− <*> parseJSON (Object (deleteKey "default" o))
1488+ <*> (parseJSON (Object (HashMap.delete "default" o)))
14961489 parseJSON _ = empty
14971490
14981491instance FromJSON Example where
@@ -1507,9 +1500,6 @@ instance FromJSON Operation where
15071500instance FromJSON PathItem where
15081501 parseJSON = sopSwaggerGenericParseJSON
15091502
1510−instance FromJSON SecurityDefinitions where
1511− parseJSON js = SecurityDefinitions <$> parseJSON js
1512−
15131503instance FromJSON RequestBody where
15141504 parseJSON = sopSwaggerGenericParseJSON
15151505
@@ -1519,8 +1509,8 @@ instance FromJSON MediaTypeObject where
15191509instance FromJSON Encoding where
15201510 parseJSON = sopSwaggerGenericParseJSON
15211511
1522−instance FromJSON Link where
1523− parseJSON = sopSwaggerGenericParseJSON
1512+instance FromJSON SecurityDefinitions where
1513+ parseJSON js = SecurityDefinitions <$> parseJSON js
15241514
15251515instance FromJSON Reference where
15261516 parseJSON (Object o) = Reference <$> o .: "$ref"
@@ -1545,38 +1535,52 @@ instance FromJSON (Referenced Response) where parseJSON = referencedParseJSON "#
15451535instance FromJSON (Referenced RequestBody) where parseJSON = referencedParseJSON "#/components/requestBodies/"
15461536instance FromJSON (Referenced Example) where parseJSON = referencedParseJSON "#/components/examples/"
15471537instance 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/"
15501538
15511539instance FromJSON Xml where
15521540 parseJSON = genericParseJSON (jsonPrefix "xml")
15531541
1542+instance FromJSON (SwaggerType 'SwaggerKindSchema) where
1543+ parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray, SwaggerNull, SwaggerObject]
1544+
1545+instance FromJSON (SwaggerType 'SwaggerKindParamOtherSchema) where
1546+ parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray, SwaggerFile]
1547+
1548+instance FromJSON (SwaggerType ('SwaggerKindNormal t)) where
1549+ parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray]
1550+
1551+instance FromJSON (CollectionFormat ('SwaggerKindNormal t)) where
1552+ parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes]
1553+
1554+-- NOTE: There aren't collections of 'Schema'
1555+--instance FromJSON (CollectionFormat (SwaggerKindSchema)) where
1556+-- parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes]
1557+
1558+instance FromJSON (CollectionFormat 'SwaggerKindParamOtherSchema) where
1559+ parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes, CollectionMulti]
1560+
1561+instance (FromJSON (SwaggerType ('SwaggerKindNormal t)), FromJSON (SwaggerItems ('SwaggerKindNormal t))) => FromJSON (ParamSchema ('SwaggerKindNormal t)) where
1562+ parseJSON = sopSwaggerGenericParseJSON
1563+instance FromJSON (ParamSchema 'SwaggerKindParamOtherSchema) where
1564+ parseJSON = sopSwaggerGenericParseJSON
1565+instance FromJSON (ParamSchema 'SwaggerKindSchema) where
1566+ parseJSON = sopSwaggerGenericParseJSON
1567+
15541568instance FromJSON AdditionalProperties where
15551569 parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
15561570 parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
15571571
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"
15681572instance HasSwaggerAesonOptions Components where
15691573 swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
15701574instance HasSwaggerAesonOptions Header where
1571− swaggerAesonOptions _ = mkSwaggerAesonOptions "header"
1572−instance AesonDefaultValue p => HasSwaggerAesonOptions (OAuth2Flow p) where
1573− swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "params"
1574−instance HasSwaggerAesonOptions OAuth2Flows where
1575− swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2Flows"
1575+ swaggerAesonOptions _ = mkSwaggerAesonOptions "header" & saoSubObject ?~ "paramSchema"
1576+instance HasSwaggerAesonOptions OAuth2Params where
1577+ swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "flow"
15761578instance HasSwaggerAesonOptions Operation where
15771579 swaggerAesonOptions _ = mkSwaggerAesonOptions "operation"
15781580instance HasSwaggerAesonOptions Param where
1579− swaggerAesonOptions _ = mkSwaggerAesonOptions "param"
1581+ swaggerAesonOptions _ = mkSwaggerAesonOptions "param" & saoSubObject ?~ "schema"
1582+instance HasSwaggerAesonOptions ParamOtherSchema where
1583+ swaggerAesonOptions _ = mkSwaggerAesonOptions "paramOtherSchema" & saoSubObject ?~ "paramSchema"
15801584instance HasSwaggerAesonOptions PathItem where
15811585 swaggerAesonOptions _ = mkSwaggerAesonOptions "pathItem"
15821586instance HasSwaggerAesonOptions Response where
@@ -1591,29 +1595,29 @@ instance HasSwaggerAesonOptions SecurityScheme where
15911595 swaggerAesonOptions _ = mkSwaggerAesonOptions "securityScheme" & saoSubObject ?~ "type"
15921596instance HasSwaggerAesonOptions Schema where
15931597 swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1594−instance HasSwaggerAesonOptions OpenApi where
1595− swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.0")]
1598+instance HasSwaggerAesonOptions Swagger where
1599+ swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.3")]
15961600instance HasSwaggerAesonOptions Example where
15971601 swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
15981602instance HasSwaggerAesonOptions Encoding where
15991603 swaggerAesonOptions _ = mkSwaggerAesonOptions "encoding"
16001604
1601−instance HasSwaggerAesonOptions Link where
1602− swaggerAesonOptions _ = mkSwaggerAesonOptions "link"
1605+instance HasSwaggerAesonOptions (ParamSchema ('SwaggerKindNormal t)) where
1606+ swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1607+instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindParamOtherSchema) where
1608+ swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1609+-- NOTE: Schema doesn't have 'items' sub object!
1610+instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindSchema) where
1611+ swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema"
16031612
1604−instance AesonDefaultValue Server
16051613instance AesonDefaultValue Components
1606−instance AesonDefaultValue OAuth2ImplicitFlow
1607−instance AesonDefaultValue OAuth2PasswordFlow
1608−instance AesonDefaultValue OAuth2ClientCredentialsFlow
1609−instance AesonDefaultValue OAuth2AuthorizationCodeFlow
1610−instance AesonDefaultValue p => AesonDefaultValue (OAuth2Flow p)
1614+instance AesonDefaultValue (ParamSchema s)
1615+instance AesonDefaultValue OAuth2Flow
16111616instance AesonDefaultValue Responses
1617+instance AesonDefaultValue ParamAnySchema
16121618instance AesonDefaultValue SecuritySchemeType
1613−instance AesonDefaultValue OpenApiType
1619+instance AesonDefaultValue (SwaggerType a)
16141620instance AesonDefaultValue MimeList where defaultValue = Just mempty
16151621instance AesonDefaultValue Info
16161622instance AesonDefaultValue ParamLocation
1617−instance AesonDefaultValue Link
1618−instance AesonDefaultValue SecurityDefinitions where
1619− defaultValue = Just mempty
1623+instance AesonDefaultValue SecurityDefinitions where defaultValue = Just $ SecurityDefinitions mempty
src/Data/OpenApi/Internal/AesonUtils.hs → src/Data/Swagger/Internal/AesonUtils.hsrenamed+13−12
@@ -5,7 +5,7 @@
55{-# LANGUAGE ExplicitForAll #-}
66{-# LANGUAGE TemplateHaskell #-}
77{-# LANGUAGE UndecidableSuperClasses #-}
8−module Data.OpenApi.Internal.AesonUtils (
8+module Data.Swagger.Internal.AesonUtils (
99 -- * Generic functions
1010 AesonDefaultValue(..),
1111 sopSwaggerGenericToJSON,
@@ -27,7 +27,7 @@ import Prelude.Compat
2727import Control.Applicative ((<|>))
2828import Control.Lens (makeLenses, (^.))
2929import Control.Monad (unless)
30−import Data.Aeson (ToJSON(..), FromJSON(..), Value(..), Object, object, (.:), (.:?), (.!=), withObject, Encoding, pairs, (.=), Series)
30+import Data.Aeson (ToJSON(..), FromJSON(..), Value(..), Object, object, (.:), (.:?), (.!=), withObject)
3131import Data.Aeson.Types (Parser, Pair)
3232import Data.Char (toLower, isUpper)
3333import Data.Foldable (traverse_)
@@ -36,11 +36,12 @@ import Data.Text (Text)
3636import Generics.SOP
3737
3838import qualified Data.Text as T
39+import qualified Data.HashMap.Strict as HM
3940import qualified Data.Set as Set
4041import qualified Data.HashMap.Strict.InsOrd as InsOrd
4142import qualified Data.HashSet.InsOrd as InsOrdHS
4243
43−import Data.OpenApi.Aeson.Compat (keyToString, objectToList, stringToKey)
44+import Data.Aeson (Encoding, pairs, (.=), Series)
4445
4546-------------------------------------------------------------------------------
4647-- SwaggerAesonOptions
@@ -48,7 +49,7 @@ import Data.OpenApi.Aeson.Compat (keyToString, objectToList, stringToKey)
4849
4950data SwaggerAesonOptions = SwaggerAesonOptions
5051 { _saoPrefix :: String
51− , _saoAdditionalPairs :: [Pair]
52+ , _saoAdditionalPairs :: [(Text, Value)]
5253 , _saoSubObject :: Maybe String
5354 }
5455
@@ -155,14 +156,14 @@ sopSwaggerGenericToJSON'' (SwaggerAesonOptions prefix _ sub) = go
155156 go Nil Nil Nil = []
156157 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
157158 | Just name' == sub = case json of
158− Object m -> objectToList m ++ rest
159+ Object m -> HM.toList m ++ rest
159160 Null -> rest
160161 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show json
161162 -- If default value: omit it.
162163 | Just x == def =
163164 rest
164165 | otherwise =
165− (stringToKey name', json) : rest
166+ (T.pack name', json) : rest
166167 where
167168 json = toJSON x
168169 name' = fieldNameModifier name
@@ -196,11 +197,11 @@ sopSwaggerGenericParseJSON = withObject "Swagger Record Object" $ \obj ->
196197 proxy = Proxy :: Proxy a
197198 opts = swaggerAesonOptions proxy
198199
199− parseAdditionalField :: Object -> Pair -> Parser ()
200+ parseAdditionalField :: Object -> (Text, Value) -> Parser ()
200201 parseAdditionalField obj (k, v) = do
201202 v' <- obj .: k
202203 unless (v == v') $ fail $
203− "Additonal field don't match for key " ++ keyToString k
204+ "Additonal field don't match for key " ++ T.unpack k
204205 ++ ": " ++ show v
205206 ++ " /= " ++ show v'
206207
@@ -231,8 +232,8 @@ sopSwaggerGenericParseJSON'' (SwaggerAesonOptions prefix _ sub) obj = go
231232 -- Note: we might strip fields of outer structure.
232233 cons <$> (withDef $ parseJSON $ Object obj) <*> rest
233234 | otherwise = case def of
234− Just def' -> cons <$> obj .:? stringToKey name' .!= def' <*> rest
235− Nothing -> cons <$> obj .: stringToKey name' <*> rest
235+ Just def' -> cons <$> obj .:? T.pack name' .!= def' <*> rest
236+ Nothing -> cons <$> obj .: T.pack name' <*> rest
236237 where
237238 cons h t = I h :* t
238239 name' = fieldNameModifier name
@@ -295,14 +296,14 @@ sopSwaggerGenericToEncoding'' (SwaggerAesonOptions prefix _ sub) = go
295296 go Nil Nil Nil = mempty
296297 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
297298 | Just name' == sub = case toJSON x of
298− Object m -> pairsToSeries (objectToList m) <> rest
299+ Object m -> pairsToSeries (HM.toList m) <> rest
299300 Null -> rest
300301 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show (toJSON x)
301302 -- If default value: omit it.
302303 | Just x == def =
303304 rest
304305 | otherwise =
305− (stringToKey name' .= x) <> rest
306+ (T.pack name' .= x) <> rest
306307 where
307308 name' = fieldNameModifier name
308309 rest = go xs names defs
src/Data/OpenApi/Internal/ParamSchema.hs → src/Data/Swagger/Internal/ParamSchema.hsrenamed+53−77
@@ -15,7 +15,7 @@
1515{-# OPTIONS_GHC -Wno-redundant-constraints #-}
1616-- For TypeErrors
1717{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
18−module Data.OpenApi.Internal.ParamSchema where
18+module Data.Swagger.Internal.ParamSchema where
1919
2020import Control.Lens
2121import Data.Aeson (ToJSON (..))
@@ -41,40 +41,34 @@ import Data.Word
4141import Data.UUID.Types (UUID)
4242import Web.Cookie (SetCookie)
4343
44−import Data.OpenApi.Internal
45−import Data.OpenApi.Lens
46−import Data.OpenApi.SchemaOptions
44+import Data.Swagger.Internal
45+import Data.Swagger.Lens
46+import Data.Swagger.SchemaOptions
4747
4848import qualified Data.ByteString as BS
49−import qualified Data.ByteString.Lazy.Char8 as BSL
49+import qualified Data.ByteString.Lazy as BSL
5050import GHC.TypeLits (TypeError, ErrorMessage(..))
5151
5252-- | Default schema for binary data (any sequence of octets).
53−binarySchema :: Schema
54−binarySchema = mempty
55− & type_ ?~ OpenApiString
53+binaryParamSchema :: ParamSchema t
54+binaryParamSchema = mempty
55+ & type_ ?~ SwaggerString
5656 & format ?~ "binary"
5757
5858-- | Default schema for binary data (base64 encoded).
59−byteSchema :: Schema
60−byteSchema = mempty
61− & type_ ?~ OpenApiString
59+byteParamSchema :: ParamSchema t
60+byteParamSchema = mempty
61+ & type_ ?~ SwaggerString
6262 & format ?~ "byte"
6363
6464-- | Default schema for password string.
6565-- @"password"@ format is used to hint UIs the input needs to be obscured.
66−passwordSchema :: Schema
67−passwordSchema = mempty
68− & type_ ?~ OpenApiString
66+passwordParamSchema :: ParamSchema t
67+passwordParamSchema = mempty
68+ & type_ ?~ SwaggerString
6969 & format ?~ "password"
7070
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.
71+-- | Convert a type into a plain @'ParamSchema'@.
7872--
7973-- An example type and instance:
8074--
@@ -87,7 +81,7 @@ passwordSchema = mempty
8781--
8882-- instance ToParamSchema Direction where
8983-- toParamSchema _ = mempty
90−-- & type_ ?~ OpenApiString
84+-- & type_ ?~ SwaggerString
9185-- & enum_ ?~ [ \"Up\", \"Down\" ]
9286-- @
9387--
@@ -112,26 +106,24 @@ passwordSchema = mempty
112106class ToParamSchema a where
113107 -- | Convert a type into a plain parameter schema.
114108 --
115− -- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy Integer)
116− -- {
117− -- "type": "integer"
118− -- }
119− toParamSchema :: Proxy a -> Schema
120− default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> Schema
109+ -- >>> encode $ toParamSchema (Proxy :: Proxy Integer)
110+ -- "{\"type\":\"integer\"}"
111+ toParamSchema :: Proxy a -> ParamSchema t
112+ default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> ParamSchema t
121113 toParamSchema = genericToParamSchema defaultSchemaOptions
122114
123115instance {-# OVERLAPPING #-} ToParamSchema String where
124− toParamSchema _ = mempty & type_ ?~ OpenApiString
116+ toParamSchema _ = mempty & type_ ?~ SwaggerString
125117
126118instance ToParamSchema Bool where
127− toParamSchema _ = mempty & type_ ?~ OpenApiBoolean
119+ toParamSchema _ = mempty & type_ ?~ SwaggerBoolean
128120
129121instance ToParamSchema Integer where
130− toParamSchema _ = mempty & type_ ?~ OpenApiInteger
122+ toParamSchema _ = mempty & type_ ?~ SwaggerInteger
131123
132124instance ToParamSchema Natural where
133125 toParamSchema _ = mempty
134− & type_ ?~ OpenApiInteger
126+ & type_ ?~ SwaggerInteger
135127 & minimum_ ?~ 0
136128 & exclusiveMinimum ?~ False
137129
@@ -157,45 +149,41 @@ instance ToParamSchema Word64 where
157149
158150-- | Default plain schema for @'Bounded'@, @'Integral'@ types.
159151--
160−-- >>> BSL.putStrLn $ encodePretty $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
161−-- {
162−-- "maximum": 127,
163−-- "minimum": -128,
164−-- "type": "integer"
165−-- }
166−toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> Schema
152+-- >>> encode $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
153+-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
154+toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> ParamSchema t
167155toParamSchemaBoundedIntegral _ = mempty
168− & type_ ?~ OpenApiInteger
156+ & type_ ?~ SwaggerInteger
169157 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
170158 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
171159
172160instance ToParamSchema Char where
173161 toParamSchema _ = mempty
174− & type_ ?~ OpenApiString
162+ & type_ ?~ SwaggerString
175163 & maxLength ?~ 1
176164 & minLength ?~ 1
177165
178166instance ToParamSchema Scientific where
179− toParamSchema _ = mempty & type_ ?~ OpenApiNumber
167+ toParamSchema _ = mempty & type_ ?~ SwaggerNumber
180168
181169instance HasResolution a => ToParamSchema (Fixed a) where
182170 toParamSchema _ = mempty
183− & type_ ?~ OpenApiNumber
171+ & type_ ?~ SwaggerNumber
184172 & multipleOf ?~ (recip . fromInteger $ resolution (Proxy :: Proxy a))
185173
186174instance ToParamSchema Double where
187175 toParamSchema _ = mempty
188− & type_ ?~ OpenApiNumber
176+ & type_ ?~ SwaggerNumber
189177 & format ?~ "double"
190178
191179instance ToParamSchema Float where
192180 toParamSchema _ = mempty
193− & type_ ?~ OpenApiNumber
181+ & type_ ?~ SwaggerNumber
194182 & format ?~ "float"
195183
196−timeParamSchema :: String -> Schema
184+timeParamSchema :: String -> ParamSchema t
197185timeParamSchema fmt = mempty
198− & type_ ?~ OpenApiString
186+ & type_ ?~ SwaggerString
199187 & format ?~ T.pack fmt
200188
201189-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
@@ -216,9 +204,9 @@ instance ToParamSchema LocalTime where
216204
217205-- |
218206-- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. format
219−-- Just "date-time"
207+-- Just "yyyy-mm-ddThh:MM:ss+hhMM"
220208instance ToParamSchema ZonedTime where
221− toParamSchema _ = timeParamSchema "date-time"
209+ toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss+hhMM"
222210
223211-- |
224212-- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. format
@@ -237,18 +225,18 @@ instance ToParamSchema TL.Text where
237225
238226instance ToParamSchema Version where
239227 toParamSchema _ = mempty
240− & type_ ?~ OpenApiString
228+ & type_ ?~ SwaggerString
241229 & pattern ?~ "^\\d+(\\.\\d+)*$"
242230
243231instance ToParamSchema SetCookie where
244232 toParamSchema _ = mempty
245− & type_ ?~ OpenApiString
233+ & type_ ?~ SwaggerString
246234
247235type family ToParamSchemaByteStringError bs where
248236 ToParamSchemaByteStringError bs = TypeError
249237 ( 'Text "Impossible to have an instance " :<>: ShowType (ToParamSchema bs) :<>: Text "."
250238 :$$: 'Text "Please, use a newtype wrapper around " :<>: ShowType bs :<>: Text " instead."
251− :$$: 'Text "Consider using byteParamSchema or binaryParamSchemaemplates." )
239+ :$$: 'Text "Consider using byteParamSchema or binaryParamSchema templates." )
252240
253241instance ToParamSchemaByteStringError BS.ByteString => ToParamSchema BS.ByteString where toParamSchema = error "impossible"
254242instance ToParamSchemaByteStringError BSL.ByteString => ToParamSchema BSL.ByteString where toParamSchema = error "impossible"
@@ -265,8 +253,8 @@ instance ToParamSchema a => ToParamSchema (Identity a) where toParamSchema _ = t
265253
266254instance ToParamSchema a => ToParamSchema [a] where
267255 toParamSchema _ = mempty
268− & type_ ?~ OpenApiArray
269− & items ?~ OpenApiItemsObject (Inline $ toParamSchema (Proxy :: Proxy a))
256+ & type_ ?~ SwaggerArray
257+ & items ?~ SwaggerItemsPrimitive Nothing (toParamSchema (Proxy :: Proxy a))
270258
271259instance ToParamSchema a => ToParamSchema (V.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
272260instance ToParamSchema a => ToParamSchema (VP.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
@@ -281,40 +269,29 @@ instance ToParamSchema a => ToParamSchema (HashSet a) where
281269 toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a))
282270
283271-- |
284−-- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy ())
285−-- {
286−-- "enum": [
287−-- "_"
288−-- ],
289−-- "type": "string"
290−-- }
272+-- >>> encode $ toParamSchema (Proxy :: Proxy ())
273+-- "{\"type\":\"string\",\"enum\":[\"_\"]}"
291274instance ToParamSchema () where
292275 toParamSchema _ = mempty
293− & type_ ?~ OpenApiString
276+ & type_ ?~ SwaggerString
294277 & enum_ ?~ ["_"]
295278
296279instance ToParamSchema UUID where
297280 toParamSchema _ = mempty
298− & type_ ?~ OpenApiString
281+ & type_ ?~ SwaggerString
299282 & format ?~ "uuid"
300283
301−-- | A configurable generic @'Schema'@ creator.
284+-- | A configurable generic @'ParamSchema'@ creator.
302285--
303286-- >>> :set -XDeriveGeneric
304287-- >>> data Color = Red | Blue deriving Generic
305−-- >>> BSL.putStrLn $ encodePretty $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
306−-- {
307−-- "enum": [
308−-- "Red",
309−-- "Blue"
310−-- ],
311−-- "type": "string"
312−-- }
313−genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> Schema
288+-- >>> encode $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
289+-- "{\"type\":\"string\",\"enum\":[\"Red\",\"Blue\"]}"
290+genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> ParamSchema t
314291genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty
315292
316293class GToParamSchema (f :: * -> *) where
317− gtoParamSchema :: SchemaOptions -> Proxy f -> Schema -> Schema
294+ gtoParamSchema :: SchemaOptions -> Proxy f -> ParamSchema t -> ParamSchema t
318295
319296instance GToParamSchema f => GToParamSchema (D1 d f) where
320297 gtoParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy f)
@@ -332,14 +309,14 @@ instance (GEnumParamSchema f, GEnumParamSchema g) => GToParamSchema (f :+: g) wh
332309 gtoParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy (f :+: g))
333310
334311class GEnumParamSchema (f :: * -> *) where
335− genumParamSchema :: SchemaOptions -> Proxy f -> Schema -> Schema
312+ genumParamSchema :: SchemaOptions -> Proxy f -> ParamSchema t -> ParamSchema t
336313
337314instance (GEnumParamSchema f, GEnumParamSchema g) => GEnumParamSchema (f :+: g) where
338315 genumParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy f) . genumParamSchema opts (Proxy :: Proxy g)
339316
340317instance Constructor c => GEnumParamSchema (C1 c U1) where
341318 genumParamSchema opts _ s = s
342− & type_ ?~ OpenApiString
319+ & type_ ?~ SwaggerString
343320 & enum_ %~ addEnumValue tag
344321 where
345322 tag = toJSON (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
@@ -351,4 +328,3 @@ data Proxy3 a b c = Proxy3
351328
352329-- $setup
353330-- >>> import Data.Aeson (encode)
354−-- >>> import Data.OpenApi.Internal.Utils
src/Data/OpenApi/Internal/Schema.hs → src/Data/Swagger/Internal/Schema.hsrenamed+172−446
@@ -1,17 +1,15 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE DataKinds #-}
42{-# LANGUAGE DefaultSignatures #-}
53{-# LANGUAGE FlexibleContexts #-}
64{-# LANGUAGE FlexibleInstances #-}
75{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE DataKinds #-}
87{-# LANGUAGE OverloadedLists #-}
98{-# LANGUAGE OverloadedStrings #-}
109{-# LANGUAGE PackageImports #-}
1110{-# LANGUAGE PolyKinds #-}
1211{-# LANGUAGE ScopedTypeVariables #-}
1312{-# LANGUAGE TupleSections #-}
14−{-# LANGUAGE TypeApplications #-}
1513{-# LANGUAGE TypeFamilies #-}
1614{-# LANGUAGE TypeOperators #-}
1715{-# LANGUAGE TypeSynonymInstances #-}
@@ -19,18 +17,17 @@
1917{-# OPTIONS_GHC -Wno-redundant-constraints #-}
2018-- For TypeErrors
2119{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
22−module Data.OpenApi.Internal.Schema where
20+module Data.Swagger.Internal.Schema where
2321
2422import Prelude ()
2523import Prelude.Compat
2624
27−import Control.Lens hiding (allOf)
25+import Control.Lens
2826import Data.Data.Lens (template)
2927
3028import Control.Monad
3129import Control.Monad.Writer
32−import Data.Aeson (Object (..), SumEncoding (..), ToJSON (..), ToJSONKey (..),
33− ToJSONKeyFunction (..), Value (..))
30+import Data.Aeson (ToJSON (..), ToJSONKey (..), ToJSONKeyFunction (..), Value (..), Object(..))
3431import Data.Char
3532import Data.Data (Data)
3633import Data.Foldable (traverse_)
@@ -42,10 +39,8 @@ import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
4239import Data.Int
4340import Data.IntSet (IntSet)
4441import Data.IntMap (IntMap)
45−import Data.List (sort)
4642import Data.List.NonEmpty.Compat (NonEmpty)
4743import Data.Map (Map)
48−import Data.Maybe (fromMaybe)
4944import Data.Proxy
5045import Data.Scientific (Scientific)
5146import Data.Fixed (Fixed, HasResolution, Pico)
@@ -62,19 +57,17 @@ import Numeric.Natural.Compat (Natural)
6257import Data.Word
6358import GHC.Generics
6459import qualified Data.UUID.Types as UUID
65−import Type.Reflection (Typeable, typeRep)
6660
67−import Data.OpenApi.Aeson.Compat (keyToText, objectKeys, toInsOrdHashMap)
68−import Data.OpenApi.Declare
69−import Data.OpenApi.Internal
70−import Data.OpenApi.Internal.ParamSchema (ToParamSchema (..))
71−import Data.OpenApi.Internal.TypeShape
72−import Data.OpenApi.Lens hiding (name, schema)
73−import qualified Data.OpenApi.Lens as Swagger
74−import Data.OpenApi.SchemaOptions
61+import Data.Swagger.Declare
62+import Data.Swagger.Internal
63+import Data.Swagger.Internal.ParamSchema (ToParamSchema(..))
64+import Data.Swagger.Lens hiding (name, schema)
65+import qualified Data.Swagger.Lens as Swagger
66+import Data.Swagger.SchemaOptions
67+import Data.Swagger.Internal.TypeShape
7568
7669import qualified Data.ByteString as BS
77−import qualified Data.ByteString.Lazy.Char8 as BSL
70+import qualified Data.ByteString.Lazy as BSL
7871import GHC.TypeLits (TypeError, ErrorMessage(..))
7972
8073unnamed :: Schema -> NamedSchema
@@ -102,7 +95,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
10295--
10396-- import Control.Lens
10497-- import Data.Proxy
105−-- import Data.OpenApi
98+-- import Data.Swagger
10699--
107100-- data Coord = Coord { x :: Double, y :: Double }
108101--
@@ -110,7 +103,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
110103-- declareNamedSchema _ = do
111104-- doubleSchema <- declareSchemaRef (Proxy :: Proxy Double)
112105-- return $ NamedSchema (Just \"Coord\") $ mempty
113−-- & type_ ?~ OpenApiObject
106+-- & type_ ?~ SwaggerObject
114107-- & properties .~
115108-- [ (\"x\", doubleSchema)
116109-- , (\"y\", doubleSchema)
@@ -136,13 +129,13 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
136129--
137130-- instance ToSchema Coord
138131-- @
139−class Typeable a => ToSchema a where
132+class ToSchema a where
140133 -- | Convert a type into an optionally named schema
141134 -- together with all used definitions.
142135 -- Note that the schema itself is included in definitions
143136 -- only if it is recursive (and thus needs its definition in scope).
144137 declareNamedSchema :: Proxy a -> Declare (Definitions Schema) NamedSchema
145− default declareNamedSchema :: (Generic a, GToSchema (Rep a)) =>
138+ default declareNamedSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted") =>
146139 Proxy a -> Declare (Definitions Schema) NamedSchema
147140 declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
148141
@@ -158,19 +151,13 @@ declareSchema = fmap _namedSchemaSchema . declareNamedSchema
158151--
159152-- >>> toNamedSchema (Proxy :: Proxy String) ^. name
160153-- Nothing
161−-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy String) ^. schema)
162−-- {
163−-- "type": "string"
164−-- }
154+-- >>> encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
155+-- "{\"type\":\"string\"}"
165156--
166157-- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
167158-- Just "Day"
168−-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy Day) ^. schema)
169−-- {
170−-- "example": "2016-07-22",
171−-- "format": "date",
172−-- "type": "string"
173−-- }
159+-- >>> encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
160+-- "{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}"
174161toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
175162toNamedSchema = undeclare . declareNamedSchema
176163
@@ -186,35 +173,22 @@ schemaName = _namedSchemaName . toNamedSchema
186173
187174-- | Convert a type into a schema.
188175--
189−-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Int8)
190−-- {
191−-- "maximum": 127,
192−-- "minimum": -128,
193−-- "type": "integer"
194−-- }
176+-- >>> encode $ toSchema (Proxy :: Proxy Int8)
177+-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
195178--
196−-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy [Day])
197−-- {
198−-- "items": {
199−-- "$ref": "#/components/schemas/Day"
200−-- },
201−-- "type": "array"
202−-- }
179+-- >>> encode $ toSchema (Proxy :: Proxy [Day])
180+-- "{\"items\":{\"$ref\":\"#/definitions/Day\"},\"type\":\"array\"}"
203181toSchema :: ToSchema a => Proxy a -> Schema
204182toSchema = _namedSchemaSchema . toNamedSchema
205183
206184-- | Convert a type into a referenced schema if possible.
207185-- Only named schemas can be referenced, nameless schemas are inlined.
208186--
209−-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Integer)
210−-- {
211−-- "type": "integer"
212−-- }
187+-- >>> encode $ toSchemaRef (Proxy :: Proxy Integer)
188+-- "{\"type\":\"integer\"}"
213189--
214−-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Day)
215−-- {
216−-- "$ref": "#/components/schemas/Day"
217−-- }
190+-- >>> encode $ toSchemaRef (Proxy :: Proxy Day)
191+-- "{\"$ref\":\"#/definitions/Day\"}"
218192toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
219193toSchemaRef = undeclare . declareSchemaRef
220194
@@ -282,15 +256,8 @@ inlineAllSchemas = inlineSchemasWhen (const True)
282256
283257-- | Convert a type into a schema without references.
284258--
285−-- >>> BSL.putStrLn $ encodePretty $ toInlinedSchema (Proxy :: Proxy [Day])
286−-- {
287−-- "items": {
288−-- "example": "2016-07-22",
289−-- "format": "date",
290−-- "type": "string"
291−-- },
292−-- "type": "array"
293−-- }
259+-- >>> encode $ toInlinedSchema (Proxy :: Proxy [Day])
260+-- "{\"items\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"},\"type\":\"array\"}"
294261--
295262-- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
296263-- when inlining recursive schemas.
@@ -320,67 +287,41 @@ inlineNonRecursiveSchemas defs = inlineSchemasWhen nonRecursive defs
320287 traverse_ usedNames (InsOrdHashMap.lookup name defs)
321288 Inline subschema -> usedNames subschema
322289
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+
323309-- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
324310-- Produced schema can be used for further refinement.
325311--
326−-- >>> BSL.putStrLn $ encodePretty $ sketchSchema "hello"
327−-- {
328−-- "example": "hello",
329−-- "type": "string"
330−-- }
312+-- >>> encode $ sketchSchema "hello"
313+-- "{\"example\":\"hello\",\"type\":\"string\"}"
331314--
332−-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (1, 2, 3)
333−-- {
334−-- "example": [
335−-- 1,
336−-- 2,
337−-- 3
338−-- ],
339−-- "items": {
340−-- "type": "number"
341−-- },
342−-- "type": "array"
343−-- }
315+-- >>> encode $ sketchSchema (1, 2, 3)
316+-- "{\"example\":[1,2,3],\"items\":{\"type\":\"number\"},\"type\":\"array\"}"
344317--
345−-- >>> BSL.putStrLn $ encodePretty $ sketchSchema ("Jack", 25)
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−-- }
318+-- >>> encode $ sketchSchema ("Jack", 25)
319+-- "{\"example\":[\"Jack\",25],\"items\":[{\"type\":\"string\"},{\"type\":\"number\"}],\"type\":\"array\"}"
361320--
362321-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
363322-- >>> instance ToJSON Person
364−-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (Person "Jack" 25)
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−-- }
323+-- >>> encode $ sketchSchema (Person "Jack" 25)
324+-- "{\"required\":[\"age\",\"name\"],\"properties\":{\"age\":{\"type\":\"number\"},\"name\":{\"type\":\"string\"}},\"example\":{\"age\":25,\"name\":\"Jack\"},\"type\":\"object\"}"
384325sketchSchema :: ToJSON a => a -> Schema
385326sketchSchema = sketch . toJSON
386327 where
@@ -388,15 +329,15 @@ sketchSchema = sketch . toJSON
388329 sketch js@(Bool _) = go js
389330 sketch js = go js & example ?~ js
390331
391− go Null = mempty & type_ ?~ OpenApiNull
392− go (Bool _) = mempty & type_ ?~ OpenApiBoolean
393− go (String _) = mempty & type_ ?~ OpenApiString
394− go (Number _) = mempty & type_ ?~ OpenApiNumber
332+ go Null = mempty & type_ ?~ SwaggerNull
333+ go (Bool _) = mempty & type_ ?~ SwaggerBoolean
334+ go (String _) = mempty & type_ ?~ SwaggerString
335+ go (Number _) = mempty & type_ ?~ SwaggerNumber
395336 go (Array xs) = mempty
396− & type_ ?~ OpenApiArray
337+ & type_ ?~ SwaggerArray
397338 & items ?~ case ischema of
398− Just s -> OpenApiItemsObject (Inline s)
399− _ -> OpenApiItemsArray (map Inline ys)
339+ Just s -> SwaggerItemsObject (Inline s)
340+ _ -> SwaggerItemsArray (map Inline ys)
400341 where
401342 ys = map go (V.toList xs)
402343 allSame = and ((zipWith (==)) ys (tail ys))
@@ -405,178 +346,64 @@ sketchSchema = sketch . toJSON
405346 (z:_) | allSame -> Just z
406347 _ -> Nothing
407348 go (Object o) = mempty
408− & type_ ?~ OpenApiObject
409− & required .~ sort (objectKeys o)
410− & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
349+ & type_ ?~ SwaggerObject
350+ & required .~ HashMap.keys o
351+ & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
411352
412353-- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
413354-- Produced schema uses as much constraints as possible.
414355--
415−-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema "hello"
416−-- {
417−-- "enum": [
418−-- "hello"
419−-- ],
420−-- "maxLength": 5,
421−-- "minLength": 5,
422−-- "pattern": "hello",
423−-- "type": "string"
424−-- }
356+-- >>> encode $ sketchStrictSchema "hello"
357+-- "{\"maxLength\":5,\"pattern\":\"hello\",\"minLength\":5,\"type\":\"string\",\"enum\":[\"hello\"]}"
425358--
426−-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (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−-- }
359+-- >>> encode $ 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]]}"
469361--
470−-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema ("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−-- }
362+-- >>> encode $ 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]]}"
503364--
504365-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
505366-- >>> instance ToJSON Person
506−-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (Person "Jack" 25)
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−-- }
367+-- >>> encode $ 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\"}]}"
542369sketchStrictSchema :: ToJSON a => a -> Schema
543370sketchStrictSchema = go . toJSON
544371 where
545− go Null = mempty & type_ ?~ OpenApiNull
372+ go Null = mempty & type_ ?~ SwaggerNull
546373 go js@(Bool _) = mempty
547− & type_ ?~ OpenApiBoolean
374+ & type_ ?~ SwaggerBoolean
548375 & enum_ ?~ [js]
549376 go js@(String s) = mempty
550− & type_ ?~ OpenApiString
377+ & type_ ?~ SwaggerString
551378 & maxLength ?~ fromIntegral (T.length s)
552379 & minLength ?~ fromIntegral (T.length s)
553380 & pattern ?~ s
554381 & enum_ ?~ [js]
555382 go js@(Number n) = mempty
556− & type_ ?~ OpenApiNumber
383+ & type_ ?~ SwaggerNumber
557384 & maximum_ ?~ n
558385 & minimum_ ?~ n
559386 & multipleOf ?~ n
560387 & enum_ ?~ [js]
561388 go js@(Array xs) = mempty
562− & type_ ?~ OpenApiArray
389+ & type_ ?~ SwaggerArray
563390 & maxItems ?~ fromIntegral sz
564391 & minItems ?~ fromIntegral sz
565− & items ?~ OpenApiItemsArray (map (Inline . go) (V.toList xs))
392+ & items ?~ SwaggerItemsArray (map (Inline . go) (V.toList xs))
566393 & uniqueItems ?~ allUnique
567394 & enum_ ?~ [js]
568395 where
569396 sz = length xs
570397 allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs))
571398 go js@(Object o) = mempty
572− & type_ ?~ OpenApiObject
573− & required .~ sort names
574− & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
399+ & type_ ?~ SwaggerObject
400+ & required .~ names
401+ & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
575402 & maxProperties ?~ fromIntegral (length names)
576403 & minProperties ?~ fromIntegral (length names)
577404 & enum_ ?~ [js]
578405 where
579− names = objectKeys o
406+ names = HashMap.keys o
580407
581408class GToSchema (f :: * -> *) where
582409 gdeclareNamedSchema :: SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
@@ -585,8 +412,8 @@ instance {-# OVERLAPPABLE #-} ToSchema a => ToSchema [a] where
585412 declareNamedSchema _ = do
586413 ref <- declareSchemaRef (Proxy :: Proxy a)
587414 return $ unnamed $ mempty
588− & type_ ?~ OpenApiArray
589− & items ?~ OpenApiItemsObject ref
415+ & type_ ?~ SwaggerArray
416+ & items ?~ SwaggerItemsObject ref
590417
591418instance {-# OVERLAPPING #-} ToSchema String where declareNamedSchema = plain . paramSchemaToSchema
592419instance ToSchema Bool where declareNamedSchema = plain . paramSchemaToSchema
@@ -611,14 +438,12 @@ instance ToSchema Scientific where declareNamedSchema = plain . paramSchemaToSc
611438instance ToSchema Double where declareNamedSchema = plain . paramSchemaToSchema
612439instance ToSchema Float where declareNamedSchema = plain . paramSchemaToSchema
613440
614−instance (Typeable (Fixed a), HasResolution a) => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
441+instance HasResolution a => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
615442
616443instance ToSchema a => ToSchema (Maybe a) where
617444 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
618445
619−instance (ToSchema a, ToSchema b) => ToSchema (Either a b) where
620− -- To match Aeson instance
621− declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions { sumEncoding = ObjectWithSingleField }
446+instance (ToSchema a, ToSchema b) => ToSchema (Either a b)
622447
623448instance ToSchema () where
624449 declareNamedSchema _ = pure (NamedSchema Nothing nullarySchema)
@@ -628,22 +453,16 @@ instance ToSchema UUID.UUID where
628453 declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p
629454 & example ?~ toJSON (UUID.toText UUID.nil)
630455
631−instance (ToSchema a, ToSchema b) => ToSchema (a, b) where
632− declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
633−instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c) where
634− declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
635−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d) where
636− declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
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
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
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
456+instance (ToSchema a, ToSchema b) => ToSchema (a, b)
457+instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c)
458+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d)
459+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e)
460+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f)
461+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g)
643462
644463timeSchema :: T.Text -> Schema
645464timeSchema fmt = mempty
646− & type_ ?~ OpenApiString
465+ & type_ ?~ SwaggerString
647466 & format ?~ fmt
648467
649468-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
@@ -658,7 +477,7 @@ instance ToSchema LocalTime where
658477 declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss"
659478 & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0))
660479
661−-- | Format @"date-time"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
480+-- | Format @"date"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
662481instance ToSchema ZonedTime where
663482 declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time"
664483 & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))
@@ -690,7 +509,7 @@ instance ToSchemaByteStringError BSL.ByteString => ToSchema BSL.ByteString where
690509instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int))
691510
692511-- | NOTE: This schema does not account for the uniqueness of keys.
693−instance (ToSchema a) => ToSchema (IntMap a) where
512+instance ToSchema a => ToSchema (IntMap a) where
694513 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
695514
696515instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
@@ -701,7 +520,7 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
701520 declareObjectMapSchema = do
702521 schema <- declareSchemaRef (Proxy :: Proxy v)
703522 return $ unnamed $ mempty
704− & type_ ?~ OpenApiObject
523+ & type_ ?~ SwaggerObject
705524 & additionalProperties ?~ AdditionalPropertiesSchema schema
706525
707526instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
@@ -709,7 +528,7 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
709528
710529instance {-# OVERLAPPING #-} ToSchema Object where
711530 declareNamedSchema _ = pure $ NamedSchema (Just "Object") $ mempty
712− & type_ ?~ OpenApiObject
531+ & type_ ?~ SwaggerObject
713532 & description ?~ "Arbitrary JSON object."
714533 & additionalProperties ?~ AdditionalPropertiesAllowed True
715534
@@ -746,15 +565,11 @@ instance ToSchema a => ToSchema (Identity a) where declareNamedSchema _ = declar
746565
747566-- | Default schema for @'Bounded'@, @'Integral'@ types.
748567--
749−-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
750−-- {
751−-- "maximum": 32767,
752−-- "minimum": -32768,
753−-- "type": "integer"
754−-- }
568+-- >>> encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
569+-- "{\"maximum\":32767,\"minimum\":-32768,\"type\":\"integer\"}"
755570toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
756571toSchemaBoundedIntegral _ = mempty
757− & type_ ?~ OpenApiInteger
572+ & type_ ?~ SwaggerInteger
758573 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
759574 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
760575
@@ -783,27 +598,8 @@ genericDeclareNamedSchemaNewtype opts f proxy = genericNameSchema opts proxy <$>
783598-- >>> instance ToSchema ButtonState
784599-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
785600-- >>> type ImageUrl = T.Text
786−-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
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−-- }
601+-- >>> encode $ 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\"}"
807603--
808604-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
809605-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -811,15 +607,15 @@ declareSchemaBoundedEnumKeyMapping :: forall map key value.
811607 (Bounded key, Enum key, ToJSONKey key, ToSchema key, ToSchema value)
812608 => Proxy (map key value) -> Declare (Definitions Schema) Schema
813609declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key of
814− ToJSONKeyText getKey _ -> objectSchema getKey
610+ ToJSONKeyText keyToText _ -> objectSchema keyToText
815611 ToJSONKeyValue _ _ -> declareSchema (Proxy :: Proxy [(key, value)])
816612 where
817− objectSchema getKey = do
613+ objectSchema keyToText = do
818614 valueRef <- declareSchemaRef (Proxy :: Proxy value)
819615 let allKeys = [minBound..maxBound :: key]
820− mkPair k = (keyToText $ getKey k, valueRef)
616+ mkPair k = (keyToText k, valueRef)
821617 return $ mempty
822− & type_ ?~ OpenApiObject
618+ & type_ ?~ SwaggerObject
823619 & properties .~ InsOrdHashMap.fromList (map mkPair allKeys)
824620
825621-- | A 'Schema' for a mapping with 'Bounded' 'Enum' keys.
@@ -830,27 +626,8 @@ declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key o
830626-- >>> instance ToSchema ButtonState
831627-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
832628-- >>> type ImageUrl = T.Text
833−-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
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−-- }
629+-- >>> encode $ 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\"}"
854631--
855632-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
856633-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -860,32 +637,32 @@ toSchemaBoundedEnumKeyMapping :: forall map key value.
860637toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping
861638
862639-- | A configurable generic @'Schema'@ creator.
863−genericDeclareSchema :: (Generic a, GToSchema (Rep a), Typeable a) =>
640+genericDeclareSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareSchemaUnrestricted") =>
864641 SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
865−genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchema opts proxy
642+genericDeclareSchema = genericDeclareSchemaUnrestricted
866643
867644-- | A configurable generic @'NamedSchema'@ creator.
868645-- This function applied to @'defaultSchemaOptions'@
869646-- is used as the default for @'declareNamedSchema'@
870647-- 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.
871653--
872−-- Default implementation will use the name from 'Typeable' instance, including concrete
873−-- instantioations of type variables.
874−--
875−-- For example:
654+-- Unlike 'genericDeclareSchema' also works for mixed sum types.
655+-- Use with care since some Swagger tools do not support well schemas for mixed sum types.
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.
876660--
877−-- >>> _namedSchemaName $ undeclare $ genericDeclareNamedSchema defaultSchemaOptions (Proxy :: Proxy (Either Int Bool))
878−-- Just "Either_Int_Bool"
879−genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a), Typeable a) =>
661+-- Unlike 'genericDeclareNamedSchema' also works for mixed sum types.
662+-- Use with care since some Swagger tools do not support well schemas for mixed sum types.
663+genericDeclareNamedSchemaUnrestricted :: forall a. (Generic a, GToSchema (Rep a)) =>
880664 SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
881−genericDeclareNamedSchema opts _ =
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−
665+genericDeclareNamedSchemaUnrestricted opts _ = gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
889666
890667-- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'.
891668genericNameSchema :: forall a d f.
@@ -901,19 +678,19 @@ gdatatypeSchemaName opts _ = case orig of
901678 orig = datatypeName (Proxy3 :: Proxy3 d f a)
902679 name = datatypeNameModifier opts orig
903680
904−-- | Construct 'NamedSchema' usinng 'ToParamSchema'.
681+-- | Lift a plain @'ParamSchema'@ into a model @'NamedSchema'@.
905682paramSchemaToNamedSchema :: (ToParamSchema a, Generic a, Rep a ~ D1 d f, Datatype d) =>
906683 SchemaOptions -> Proxy a -> NamedSchema
907684paramSchemaToNamedSchema opts proxy = genericNameSchema opts proxy (paramSchemaToSchema proxy)
908685
909−-- | Construct 'Schema' usinng 'ToParamSchema'.
686+-- | Lift a plain @'ParamSchema'@ into a model @'Schema'@.
910687paramSchemaToSchema :: ToParamSchema a => Proxy a -> Schema
911−paramSchemaToSchema = toParamSchema
688+paramSchemaToSchema proxy = mempty & paramSchema .~ toParamSchema proxy
912689
913690nullarySchema :: Schema
914691nullarySchema = mempty
915− & type_ ?~ OpenApiArray
916− & items ?~ OpenApiItemsArray []
692+ & type_ ?~ SwaggerArray
693+ & items ?~ SwaggerItemsArray []
917694
918695gtoNamedSchema :: GToSchema f => SchemaOptions -> Proxy f -> NamedSchema
919696gtoNamedSchema opts proxy = undeclare $ gdeclareNamedSchema opts proxy mempty
@@ -943,15 +720,12 @@ instance (Selector s, GToSchema f, GToSchema (S1 s f)) => GToSchema (C1 c (S1 s
943720 | unwrapUnaryRecords opts = fieldSchema
944721 | otherwise =
945722 case schema ^. items of
946− Just (OpenApiItemsArray [_]) -> fieldSchema
723+ Just (SwaggerItemsArray [_]) -> fieldSchema
947724 _ -> do
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
952− return (unnamed schema')
725+ declare defs
726+ return (unnamed schema)
953727 where
954− (_, NamedSchema _ schema) = runDeclare recordSchema mempty
728+ (defs, NamedSchema _ schema) = runDeclare recordSchema mempty
955729 recordSchema = gdeclareNamedSchema opts (Proxy :: Proxy (S1 s f)) s
956730 fieldSchema = gdeclareNamedSchema opts (Proxy :: Proxy f) s
957731
@@ -974,10 +748,10 @@ gdeclareSchemaRef opts proxy = do
974748 return $ Ref (Reference name)
975749 _ -> Inline <$> gdeclareSchema opts proxy
976750
977−appendItem :: Referenced Schema -> Maybe OpenApiItems -> Maybe OpenApiItems
978−appendItem x Nothing = Just (OpenApiItemsArray [x])
979−appendItem x (Just (OpenApiItemsArray xs)) = Just (OpenApiItemsArray (xs ++ [x]))
980−appendItem _ _ = error "GToSchema.appendItem: cannot append to OpenApiItemsObject"
751+appendItem :: Referenced Schema -> Maybe (SwaggerItems 'SwaggerKindSchema) -> Maybe (SwaggerItems 'SwaggerKindSchema)
752+appendItem x Nothing = Just (SwaggerItemsArray [x])
753+appendItem x (Just (SwaggerItemsArray xs)) = Just (SwaggerItemsArray (xs ++ [x]))
754+appendItem _ _ = error "GToSchema.appendItem: cannot append to SwaggerItemsObject"
981755
982756withFieldSchema :: forall proxy s f. (Selector s, GToSchema f) =>
983757 SchemaOptions -> proxy s f -> Bool -> Schema -> Declare (Definitions Schema) Schema
@@ -986,12 +760,12 @@ withFieldSchema opts _ isRequiredField schema = do
986760 return $
987761 if T.null fname
988762 then schema
989− & type_ ?~ OpenApiArray
763+ & type_ ?~ SwaggerArray
990764 & items %~ appendItem ref
991765 & maxItems %~ Just . maybe 1 (+1) -- increment maxItems
992766 & minItems %~ Just . maybe 1 (+1) -- increment minItems
993767 else schema
994− & type_ ?~ OpenApiObject
768+ & type_ ?~ SwaggerObject
995769 & properties . at fname ?~ ref
996770 & if isRequiredField
997771 then required %~ (++ [fname])
@@ -1017,110 +791,62 @@ instance ( GSumToSchema f
1017791 , GSumToSchema g
1018792 ) => GToSchema (f :+: g)
1019793 where
1020− -- Aeson does not unwrap unary record in sum types.
1021− gdeclareNamedSchema opts p s = gdeclareNamedSumSchema (opts { unwrapUnaryRecords = False } )p s
794+ gdeclareNamedSchema = gdeclareNamedSumSchema
1022795
1023796gdeclareNamedSumSchema :: GSumToSchema f => SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
1024−gdeclareNamedSumSchema opts proxy _
1025− | allNullaryToStringTag opts && allNullary = pure $ unnamed (toStringTag sumSchemas)
1026− | otherwise = do
1027− (schemas, _) <- runWriterT declareSumSchema
1028− return $ unnamed $ mempty
1029− & type_ ?~ OpenApiObject
1030− & oneOf ?~ (snd <$> schemas)
797+gdeclareNamedSumSchema opts proxy s
798+ | allNullaryToStringTag opts && allNullary = pure $ unnamed (toStringTag sumSchema)
799+ | otherwise = (unnamed . fst) <$> runWriterT declareSumSchema
1031800 where
1032− declareSumSchema = gsumToSchema opts proxy
1033− (sumSchemas, All allNullary) = undeclare (runWriterT declareSumSchema)
801+ declareSumSchema = gsumToSchema opts proxy s
802+ (sumSchema, All allNullary) = undeclare (runWriterT declareSumSchema)
1034803
1035− toStringTag schemas = mempty
1036− & type_ ?~ OpenApiString
1037− & enum_ ?~ map (String . fst) sumSchemas
804+ toStringTag schema = mempty
805+ & type_ ?~ SwaggerString
806+ & enum_ ?~ map toJSON (schema ^.. properties.ifolded.asIndex)
1038807
1039808type AllNullary = All
1040809
1041810class GSumToSchema (f :: * -> *) where
1042− gsumToSchema :: SchemaOptions -> Proxy f -> WriterT AllNullary (Declare (Definitions Schema)) [(T.Text, Referenced Schema)]
811+ gsumToSchema :: SchemaOptions -> Proxy f -> Schema -> WriterT AllNullary (Declare (Definitions Schema)) Schema
1043812
1044813instance (GSumToSchema f, GSumToSchema g) => GSumToSchema (f :+: g) where
1045− gsumToSchema opts _ =
1046− (<>) <$> gsumToSchema opts (Proxy :: Proxy f) <*> gsumToSchema opts (Proxy :: Proxy g)
814+ gsumToSchema opts _ = gsumToSchema opts (Proxy :: Proxy f) >=> gsumToSchema opts (Proxy :: Proxy g)
1047815
1048−-- | Convert one component of the sum to schema, to be later combined with @oneOf@.
1049816gsumConToSchemaWith :: forall c f. (GToSchema (C1 c f), Constructor c) =>
1050− Maybe (Referenced Schema) -> SchemaOptions -> Proxy (C1 c f) -> (T.Text, Referenced Schema)
1051−gsumConToSchemaWith ref opts _ = (tag, schema)
817+ Referenced Schema -> SchemaOptions -> Proxy (C1 c f) -> Schema -> Schema
818+gsumConToSchemaWith ref opts _ schema = schema
819+ & type_ ?~ SwaggerObject
820+ & properties . at tag ?~ ref
821+ & maxProperties ?~ 1
822+ & minProperties ?~ 1
1052823 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−
1089824 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
1093825
1094826gsumConToSchema :: (GToSchema (C1 c f), Constructor c) =>
1095− SchemaOptions -> Proxy (C1 c f) -> Declare (Definitions Schema) [(T.Text, Referenced Schema)]
1096−gsumConToSchema opts proxy = do
827+ SchemaOptions -> Proxy (C1 c f) -> Schema -> Declare (Definitions Schema) Schema
828+gsumConToSchema opts proxy schema = do
1097829 ref <- gdeclareSchemaRef opts proxy
1098− return [gsumConToSchemaWith (Just ref) opts proxy]
830+ return $ gsumConToSchemaWith ref opts proxy schema
1099831
1100832instance {-# OVERLAPPABLE #-} (Constructor c, GToSchema f) => GSumToSchema (C1 c f) where
1101− gsumToSchema opts proxy = do
833+ gsumToSchema opts proxy schema = do
1102834 tell (All False)
1103− lift $ gsumConToSchema opts proxy
835+ lift $ gsumConToSchema opts proxy schema
1104836
1105837instance (Constructor c, Selector s, GToSchema f) => GSumToSchema (C1 c (S1 s f)) where
1106− gsumToSchema opts proxy = do
838+ gsumToSchema opts proxy schema = do
1107839 tell (All False)
1108− lift $ gsumConToSchema opts proxy
840+ lift $ gsumConToSchema opts proxy schema
1109841
1110842instance Constructor c => GSumToSchema (C1 c U1) where
1111− gsumToSchema opts proxy = pure $ (:[]) $ gsumConToSchemaWith Nothing opts proxy
843+ gsumToSchema opts proxy = pure . gsumConToSchemaWith (Inline nullarySchema) opts proxy
1112844
1113845data Proxy2 a b = Proxy2
1114846
1115847data Proxy3 a b c = Proxy3
1116848
1117−{- $setup
1118−>>> import Data.OpenApi
1119−>>> import Data.Aeson (encode)
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−-}
849+-- $setup
850+-- >>> import Data.Swagger
851+-- >>> import Data.Aeson (encode)
852+-- >>> import Data.Aeson.Types (toJSONKeyText)
src/Data/OpenApi/Internal/Schema/Validation.hs → src/Data/Swagger/Internal/Schema/Validation.hsrenamed+98−119
@@ -1,5 +1,4 @@
11{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE CPP #-}
32{-# LANGUAGE DataKinds #-}
43{-# LANGUAGE DeriveFunctor #-}
54{-# LANGUAGE FlexibleInstances #-}
@@ -13,33 +12,29 @@
1312{-# LANGUAGE ScopedTypeVariables #-}
1413{-# LANGUAGE TemplateHaskell #-}
1514{-# LANGUAGE UndecidableInstances #-}
16−{-# LANGUAGE ViewPatterns #-}
1715-- |
18−-- Module: Data.OpenApi.Internal.Schema.Validation
16+-- Module: Data.Swagger.Internal.Schema.Validation
1917-- Copyright: (c) 2015 GetShopTV
2018-- License: BSD3
2119-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
2220-- Stability: experimental
2321--
2422-- Validate JSON values with Swagger Schema.
25−module Data.OpenApi.Internal.Schema.Validation where
23+module Data.Swagger.Internal.Schema.Validation where
2624
2725import Prelude ()
2826import Prelude.Compat
2927
3028import Control.Applicative
31−import Control.Lens hiding (allOf)
32−import Control.Monad (forM, forM_, when)
29+import Control.Lens
30+import Control.Monad (when)
3331
3432import Data.Aeson hiding (Result)
35−#if MIN_VERSION_aeson(2,0,0)
36−import qualified Data.Aeson.KeyMap as KeyMap
37−#endif
33+import Data.Aeson.Encode.Pretty (encodePretty)
3834import Data.Foldable (for_, sequenceA_,
3935 traverse_)
40−#if !MIN_VERSION_aeson(2,0,0)
4136import Data.HashMap.Strict (HashMap)
42−#endif
37+import qualified Data.HashMap.Strict as HashMap
4338import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
4439import qualified "unordered-containers" Data.HashSet as HashSet
4540import Data.Maybe (fromMaybe)
@@ -52,12 +47,10 @@ import qualified Data.Text.Lazy.Encoding as TL
5247import Data.Vector (Vector)
5348import qualified Data.Vector as Vector
5449
55−import Data.OpenApi.Aeson.Compat (hasKey, keyToText, lookupKey, objectToList)
56−import Data.OpenApi.Declare
57−import Data.OpenApi.Internal
58−import Data.OpenApi.Internal.Schema
59−import Data.OpenApi.Internal.Utils
60−import Data.OpenApi.Lens
50+import Data.Swagger.Declare
51+import Data.Swagger.Internal
52+import Data.Swagger.Internal.Schema
53+import Data.Swagger.Lens
6154
6255-- | Validate @'ToJSON'@ instance matches @'ToSchema'@ for a given value.
6356-- This can be used with QuickCheck to ensure those instances are coherent:
@@ -109,33 +102,33 @@ validateToJSONWithPatternChecker checker = validateJSONWithPatternChecker checke
109102-- <BLANKLINE>
110103-- Swagger Schema:
111104-- {
112−-- "properties": {
113−-- "name": {
114−-- "type": "string"
115−-- },
116−-- "phone": {
117−-- "$ref": "#/components/schemas/Phone"
118−-- }
119−-- },
120105-- "required": [
121106-- "name",
122107-- "phone"
123108-- ],
124−-- "type": "object"
109+-- "type": "object",
110+-- "properties": {
111+-- "phone": {
112+-- "$ref": "#/definitions/Phone"
113+-- },
114+-- "name": {
115+-- "type": "string"
116+-- }
117+-- }
125118-- }
126119-- <BLANKLINE>
127120-- Swagger Description Context:
128121-- {
129122-- "Phone": {
123+-- "required": [
124+-- "value"
125+-- ],
126+-- "type": "object",
130127-- "properties": {
131128-- "value": {
132129-- "type": "string"
133130-- }
134−-- },
135−-- "required": [
136−-- "value"
137−-- ],
138−-- "type": "object"
131+-- }
139132-- }
140133-- }
141134-- <BLANKLINE>
@@ -211,7 +204,7 @@ instance Monad Result where
211204
212205-- | Validation configuration.
213206data Config = Config
214− { -- | Pattern checker for @'_schemaPattern'@ validation.
207+ { -- | Pattern checker for @'_paramSchemaPattern'@ validation.
215208 configPatternChecker :: Pattern -> Text -> Bool
216209 -- | Schema definitions in scope to resolve references.
217210 , configDefinitions :: Definitions Schema
@@ -304,34 +297,40 @@ validateWithSchemaRef (Inline s) js = sub s (validateWithSchema js)
304297
305298-- | Validate JSON @'Value'@ with Swagger @'Schema'@.
306299validateWithSchema :: Value -> Validation Schema ()
307−validateWithSchema val = do
308− validateSchemaType val
309− validateEnum val
300+validateWithSchema value = do
301+ validateSchemaType value
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
310309
311−validateInteger :: Scientific -> Validation Schema ()
310+validateInteger :: Scientific -> Validation (ParamSchema t) ()
312311validateInteger n = do
313312 when (not (isInteger n)) $
314313 invalid ("not an integer")
315314 validateNumber n
316315
317−validateNumber :: Scientific -> Validation Schema ()
316+validateNumber :: Scientific -> Validation (ParamSchema t) ()
318317validateNumber n = withConfig $ \_cfg -> withSchema $ \sch -> do
319318 let exMax = Just True == sch ^. exclusiveMaximum
320319 exMin = Just True == sch ^. exclusiveMinimum
321320
322321 check maximum_ $ \m ->
323322 when (if exMax then (n >= m) else (n > m)) $
324− invalid ("value " ++ show n ++ " exceeds maximum (should be " ++ (if exMax then "<" else "<=") ++ show m ++ ")")
323+ invalid ("value " ++ show n ++ " exceeds maximum (should be " ++ if exMax then "<" else "<=" ++ show m ++ ")")
325324
326325 check minimum_ $ \m ->
327326 when (if exMin then (n <= m) else (n < m)) $
328− invalid ("value " ++ show n ++ " falls below minimum (should be " ++ (if exMin then ">" else ">=") ++ show m ++ ")")
327+ invalid ("value " ++ show n ++ " falls below minimum (should be " ++ if exMin then ">" else ">=" ++ show m ++ ")")
329328
330329 check multipleOf $ \k ->
331330 when (not (isInteger (n / k))) $
332331 invalid ("expected a multiple of " ++ show k ++ " but got " ++ show n)
333332
334−validateString :: Text -> Validation Schema ()
333+validateString :: Text -> Validation (ParamSchema t) ()
335334validateString s = do
336335 check maxLength $ \n ->
337336 when (len > fromInteger n) $
@@ -348,7 +347,7 @@ validateString s = do
348347 where
349348 len = Text.length s
350349
351−validateArray :: Vector Value -> Validation Schema ()
350+validateArray :: Vector Value -> Validation (ParamSchema t) ()
352351validateArray xs = do
353352 check maxItems $ \n ->
354353 when (len > fromInteger n) $
@@ -359,8 +358,9 @@ validateArray xs = do
359358 invalid ("array is too short (size should be >=" ++ show n ++ ")")
360359
361360 check items $ \case
362− OpenApiItemsObject itemSchema -> traverse_ (validateWithSchemaRef itemSchema) xs
363− OpenApiItemsArray itemSchemas -> do
361+ SwaggerItemsPrimitive _ itemSchema -> sub itemSchema $ traverse_ validateWithParamSchema xs
362+ SwaggerItemsObject itemSchema -> traverse_ (validateWithSchemaRef itemSchema) xs
363+ SwaggerItemsArray itemSchemas -> do
364364 when (len /= length itemSchemas) $
365365 invalid ("array size is invalid (should be exactly " ++ show (length itemSchemas) ++ ")")
366366 sequenceA_ (zipWith validateWithSchemaRef itemSchemas (Vector.toList xs))
@@ -372,16 +372,10 @@ validateArray xs = do
372372 len = Vector.length xs
373373 allUnique = len == HashSet.size (HashSet.fromList (Vector.toList xs))
374374
375−validateObject ::
376−#if MIN_VERSION_aeson(2,0,0)
377− KeyMap.KeyMap Value
378−#else
379− HashMap Text Value
380−#endif
381− -> Validation Schema ()
375+validateObject :: HashMap Text Value -> Validation Schema ()
382376validateObject o = withSchema $ \sch ->
383377 case sch ^. discriminator of
384− Just (Discriminator pname types) -> case fromJSON <$> lookupKey pname o of
378+ Just (Discriminator pname types) -> case fromJSON <$> HashMap.lookup pname o of
385379 Just (Success pvalue) ->
386380 let ref = fromMaybe pvalue $ InsOrdHashMap.lookup pvalue types
387381 -- TODO ref may be name or reference
@@ -400,15 +394,15 @@ validateObject o = withSchema $ \sch ->
400394 validateRequired
401395 validateProps
402396 where
403− size = fromIntegral (length o)
397+ size = fromIntegral (HashMap.size o)
404398
405399 validateRequired = withSchema $ \sch -> traverse_ validateReq (sch ^. required)
406400 validateReq n =
407− when (not (hasKey n o)) $
401+ when (not (HashMap.member n o)) $
408402 invalid ("property " ++ show n ++ " is required, but not found in " ++ show (encode o))
409403
410404 validateProps = withSchema $ \sch -> do
411− for_ (objectToList o) $ \(keyToText -> k, v) ->
405+ for_ (HashMap.toList o) $ \(k, v) ->
412406 case v of
413407 Null | not (k `elem` (sch ^. required)) -> valid -- null is fine for non-required property
414408 _ ->
@@ -424,21 +418,21 @@ validateObject o = withSchema $ \sch ->
424418 unknownProperty pname = invalid $
425419 "property " <> show pname <> " is found in JSON value, but it is not mentioned in Swagger schema"
426420
427−validateEnum :: Value -> Validation Schema ()
428−validateEnum val = do
421+validateEnum :: Value -> Validation (ParamSchema t) ()
422+validateEnum value = do
429423 check enum_ $ \xs ->
430− when (val `notElem` xs) $
431− invalid ("expected one of " ++ show (encode xs) ++ " but got " ++ show val)
424+ when (value `notElem` xs) $
425+ invalid ("expected one of " ++ show (encode xs) ++ " but got " ++ show value)
432426
433427-- | Infer schema type based on used properties.
434428--
435429-- This is like 'inferParamSchemaTypes', but also works for objects:
436430--
437431-- >>> inferSchemaTypes <$> decode "{\"minProperties\": 1}"
438−-- Just [OpenApiObject]
439−inferSchemaTypes :: Schema -> [OpenApiType]
440−inferSchemaTypes sch = inferParamSchemaTypes sch ++
441− [ OpenApiObject | any ($ sch)
432+-- Just [SwaggerObject]
433+inferSchemaTypes :: Schema -> [SwaggerType 'SwaggerKindSchema]
434+inferSchemaTypes sch = inferParamSchemaTypes (sch ^. paramSchema) ++
435+ [ SwaggerObject | any ($ sch)
442436 [ has (additionalProperties._Just)
443437 , has (maxProperties._Just)
444438 , has (minProperties._Just)
@@ -448,77 +442,62 @@ inferSchemaTypes sch = inferParamSchemaTypes sch ++
448442-- | Infer schema type based on used properties.
449443--
450444-- >>> inferSchemaTypes <$> decode "{\"minLength\": 2}"
451−-- Just [OpenApiString]
445+-- Just [SwaggerString]
452446--
453447-- >>> inferSchemaTypes <$> decode "{\"maxItems\": 0}"
454−-- Just [OpenApiArray]
448+-- Just [SwaggerArray]
455449--
456−-- From numeric properties 'OpenApiInteger' type is inferred.
457−-- If you want 'OpenApiNumber' instead, you must specify it explicitly.
450+-- From numeric properties 'SwaggerInteger' type is inferred.
451+-- If you want 'SwaggerNumber' instead, you must specify it explicitly.
458452--
459453-- >>> inferSchemaTypes <$> decode "{\"minimum\": 1}"
460−-- Just [OpenApiInteger]
461−inferParamSchemaTypes :: Schema -> [OpenApiType]
454+-- Just [SwaggerInteger]
455+inferParamSchemaTypes :: ParamSchema t -> [SwaggerType t]
462456inferParamSchemaTypes sch = concat
463− [ [ OpenApiArray | any ($ sch)
457+ [ [ SwaggerArray | any ($ sch)
464458 [ has (items._Just)
465459 , has (maxItems._Just)
466460 , has (minItems._Just)
467461 , has (uniqueItems._Just) ] ]
468− , [ OpenApiInteger | any ($ sch)
462+ , [ SwaggerInteger | any ($ sch)
469463 [ has (exclusiveMaximum._Just)
470464 , has (exclusiveMinimum._Just)
471465 , has (maximum_._Just)
472466 , has (minimum_._Just)
473467 , has (multipleOf._Just) ] ]
474− , [ OpenApiString | any ($ sch)
468+ , [ SwaggerString | any ($ sch)
475469 [ has (maxLength._Just)
476470 , has (minLength._Just)
477471 , has (pattern._Just) ] ]
478472 ]
479473
480474validateSchemaType :: Value -> Validation Schema ()
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− _ ->
497− case (sch ^. type_, val) of
498− (Just OpenApiNull, Null) -> valid
499− (Just OpenApiBoolean, Bool _) -> valid
500− (Just OpenApiInteger, Number n) -> validateInteger n
501− (Just OpenApiNumber, Number n) -> validateNumber n
502− (Just OpenApiString, String s) -> validateString s
503− (Just OpenApiArray, Array xs) -> validateArray xs
504− (Just OpenApiObject, Object o) -> validateObject o
505− (Nothing, Null) -> valid
506− (Nothing, Bool _) -> valid
507− -- Number by default
508− (Nothing, Number n) -> validateNumber n
509− (Nothing, String s) -> validateString s
510− (Nothing, Array xs) -> validateArray xs
511− (Nothing, Object o) -> validateObject o
512− bad -> invalid $ "expected JSON value of type " ++ showType bad
513−
514−validateParamSchemaType :: Value -> Validation Schema ()
515−validateParamSchemaType val = withSchema $ \sch ->
516− case (sch ^. type_, val) of
517− (Just OpenApiBoolean, Bool _) -> valid
518− (Just OpenApiInteger, Number n) -> validateInteger n
519− (Just OpenApiNumber, Number n) -> validateNumber n
520− (Just OpenApiString, String s) -> validateString s
521− (Just OpenApiArray, Array xs) -> validateArray xs
475+validateSchemaType value = withSchema $ \sch ->
476+ case (sch ^. type_, value) of
477+ (Just SwaggerNull, Null) -> valid
478+ (Just SwaggerBoolean, Bool _) -> valid
479+ (Just SwaggerInteger, Number n) -> sub_ paramSchema (validateInteger n)
480+ (Just SwaggerNumber, Number n) -> sub_ paramSchema (validateNumber n)
481+ (Just SwaggerString, String s) -> sub_ paramSchema (validateString s)
482+ (Just SwaggerArray, Array xs) -> sub_ paramSchema (validateArray xs)
483+ (Just SwaggerObject, Object o) -> validateObject o
484+ (Nothing, Null) -> valid
485+ (Nothing, Bool _) -> valid
486+ -- Number by default
487+ (Nothing, Number n) -> sub_ paramSchema (validateNumber n)
488+ (Nothing, String s) -> sub_ paramSchema (validateString s)
489+ (Nothing, Array xs) -> sub_ paramSchema (validateArray xs)
490+ (Nothing, Object o) -> validateObject o
491+ bad -> invalid $ "expected JSON value of type " ++ showType bad
492+
493+validateParamSchemaType :: Value -> Validation (ParamSchema t) ()
494+validateParamSchemaType value = withSchema $ \sch ->
495+ case (sch ^. type_, value) of
496+ (Just SwaggerBoolean, Bool _) -> valid
497+ (Just SwaggerInteger, Number n) -> validateInteger n
498+ (Just SwaggerNumber, Number n) -> validateNumber n
499+ (Just SwaggerString, String s) -> validateString s
500+ (Just SwaggerArray, Array xs) -> validateArray xs
522501 (Nothing, Bool _) -> valid
523502 -- Number by default
524503 (Nothing, Number n) -> validateNumber n
@@ -526,11 +505,11 @@ validateParamSchemaType val = withSchema $ \sch ->
526505 (Nothing, Array xs) -> validateArray xs
527506 bad -> invalid $ "expected JSON value of type " ++ showType bad
528507
529−showType :: (Maybe OpenApiType, Value) -> String
508+showType :: (Maybe (SwaggerType t), Value) -> String
530509showType (Just ty, _) = show ty
531−showType (Nothing, Null) = "OpenApiNull"
532−showType (Nothing, Bool _) = "OpenApiBoolean"
533−showType (Nothing, Number _) = "OpenApiNumber"
534−showType (Nothing, String _) = "OpenApiString"
535−showType (Nothing, Array _) = "OpenApiArray"
536−showType (Nothing, Object _) = "OpenApiObject"
510+showType (Nothing, Null) = "SwaggerNull"
511+showType (Nothing, Bool _) = "SwaggerBoolean"
512+showType (Nothing, Number _) = "SwaggerNumber"
513+showType (Nothing, String _) = "SwaggerString"
514+showType (Nothing, Array _) = "SwaggerArray"
515+showType (Nothing, Object _) = "SwaggerObject"
src/Data/OpenApi/Internal/TypeShape.hs → src/Data/Swagger/Internal/TypeShape.hsrenamed+1−1
@@ -4,7 +4,7 @@
44{-# LANGUAGE TypeOperators #-}
55{-# LANGUAGE UndecidableInstances #-}
66
7−module Data.OpenApi.Internal.TypeShape where
7+module Data.Swagger.Internal.TypeShape where
88
99import Data.Proxy
1010import GHC.Generics
src/Data/OpenApi/Internal/Utils.hs → src/Data/Swagger/Internal/Utils.hsrenamed+1−6
@@ -4,7 +4,7 @@
44{-# LANGUAGE OverloadedStrings #-}
55{-# LANGUAGE RankNTypes #-}
66{-# LANGUAGE TypeOperators #-}
7−module Data.OpenApi.Internal.Utils where
7+module Data.Swagger.Internal.Utils where
88
99import Prelude ()
1010import Prelude.Compat
@@ -13,8 +13,6 @@ import Control.Lens ((&), (%~))
1313import Control.Lens.TH
1414import Data.Aeson
1515import Data.Aeson.Types
16−import qualified Data.Aeson.Encode.Pretty as P
17−import qualified Data.ByteString.Lazy as BSL
1816import Data.Char
1917import Data.Data
2018import Data.Hashable (Hashable)
@@ -134,6 +132,3 @@ instance SwaggerMonoid (Maybe a) where
134132 swaggerMempty = Nothing
135133 swaggerMappend x Nothing = x
136134 swaggerMappend _ y = y
137−
138−encodePretty :: ToJSON a => a -> BSL.ByteString
139−encodePretty = P.encodePretty' $ P.defConfig { P.confCompare = P.compare }
src/Data/Swagger/Lens.hsnew file+187
@@ -0,0 +1,187 @@
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 ''Host
37+makeFields ''Info
38+makeFields ''Contact
39+makeFields ''License
40+makeLensesWith swaggerFieldRules ''PathItem
41+makeFields ''Tag
42+makeFields ''Operation
43+makeFields ''Param
44+makeLensesWith swaggerFieldRules ''ParamOtherSchema
45+makeFields ''Header
46+makeLensesWith swaggerFieldRules ''Schema
47+makeFields ''NamedSchema
48+makeLensesWith swaggerFieldRules ''ParamSchema
49+makeFields ''Xml
50+makeLensesWith swaggerFieldRules ''Responses
51+makeFields ''Response
52+makeLensesWith swaggerFieldRules ''SecurityScheme
53+makeFields ''ApiKeyParams
54+makeFields ''OAuth2Params
55+makeFields ''ExternalDocs
56+makeFields ''Encoding
57+makeFields ''Example
58+makeFields ''Discriminator
59+
60+-- * Prisms
61+-- ** 'ParamAnySchema' prisms
62+makePrisms ''ParamAnySchema
63+-- ** 'SecuritySchemeType' prisms
64+makePrisms ''SecuritySchemeType
65+-- ** 'Referenced' prisms
66+makePrisms ''Referenced
67+
68+-- ** 'SwaggerItems' prisms
69+
70+_SwaggerItemsArray :: Review (SwaggerItems 'SwaggerKindSchema) [Referenced Schema]
71+_SwaggerItemsArray
72+ = unto (\x -> SwaggerItemsArray x)
73+{- \x -> case x of
74+ SwaggerItemsPrimitive c p -> Left (SwaggerItemsPrimitive c p)
75+ SwaggerItemsObject o -> Left (SwaggerItemsObject o)
76+ SwaggerItemsArray a -> Right a
77+-}
78+
79+_SwaggerItemsObject :: Review (SwaggerItems 'SwaggerKindSchema) (Referenced Schema)
80+_SwaggerItemsObject
81+ = unto (\x -> SwaggerItemsObject x)
82+{- \x -> case x of
83+ SwaggerItemsPrimitive c p -> Left (SwaggerItemsPrimitive c p)
84+ SwaggerItemsObject o -> Right o
85+ SwaggerItemsArray a -> Left (SwaggerItemsArray a)
86+-}
87+
88+_SwaggerItemsPrimitive :: forall t p f. (Profunctor p, Bifunctor p, Functor f) => Optic' p f (SwaggerItems t) (Maybe (CollectionFormat t), ParamSchema t)
89+_SwaggerItemsPrimitive = unto (\(c, p) -> SwaggerItemsPrimitive c p)
90+
91+-- =============================================================
92+-- More helpful instances for easier access to schema properties
93+
94+type instance Index Responses = HttpStatusCode
95+type instance Index Operation = HttpStatusCode
96+
97+type instance IxValue Responses = Referenced Response
98+type instance IxValue Operation = Referenced Response
99+
100+instance Ixed Responses where ix n = responses . ix n
101+instance At Responses where at n = responses . at n
102+
103+instance Ixed Operation where ix n = responses . ix n
104+instance At Operation where at n = responses . at n
105+
106+type instance Index SecurityDefinitions = Text
107+type instance IxValue SecurityDefinitions = SecurityScheme
108+
109+instance Ixed SecurityDefinitions where ix n = (coerced :: Lens' SecurityDefinitions (Definitions SecurityScheme)). ix n
110+instance At SecurityDefinitions where at n = (coerced :: Lens' SecurityDefinitions (Definitions SecurityScheme)). at n
111+
112+instance HasParamSchema NamedSchema (ParamSchema 'SwaggerKindSchema) where paramSchema = schema.paramSchema
113+
114+-- HasType instances
115+instance HasType Header (Maybe (SwaggerType ('SwaggerKindNormal Header))) where type_ = paramSchema.type_
116+instance HasType Schema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
117+instance HasType NamedSchema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
118+instance HasType ParamOtherSchema (Maybe (SwaggerType 'SwaggerKindParamOtherSchema)) where type_ = paramSchema.type_
119+
120+-- HasDefault instances
121+instance HasDefault Header (Maybe Value) where default_ = paramSchema.default_
122+instance HasDefault Schema (Maybe Value) where default_ = paramSchema.default_
123+instance HasDefault ParamOtherSchema (Maybe Value) where default_ = paramSchema.default_
124+
125+-- OVERLAPPABLE instances
126+
127+instance
128+ {-# OVERLAPPABLE #-}
129+ HasParamSchema s (ParamSchema t)
130+ => HasFormat s (Maybe Format) where
131+ format = paramSchema.format
132+
133+instance
134+ {-# OVERLAPPABLE #-}
135+ HasParamSchema s (ParamSchema t)
136+ => HasItems s (Maybe (SwaggerItems t)) where
137+ items = paramSchema.items
138+
139+instance
140+ {-# OVERLAPPABLE #-}
141+ HasParamSchema s (ParamSchema t)
142+ => HasMaximum s (Maybe Scientific) where
143+ maximum_ = paramSchema.maximum_
144+
145+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
146+ => HasExclusiveMaximum s (Maybe Bool) where
147+ exclusiveMaximum = paramSchema.exclusiveMaximum
148+
149+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
150+ => HasMinimum s (Maybe Scientific) where
151+ minimum_ = paramSchema.minimum_
152+
153+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
154+ => HasExclusiveMinimum s (Maybe Bool) where
155+ exclusiveMinimum = paramSchema.exclusiveMinimum
156+
157+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
158+ => HasMaxLength s (Maybe Integer) where
159+ maxLength = paramSchema.maxLength
160+
161+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
162+ => HasMinLength s (Maybe Integer) where
163+ minLength = paramSchema.minLength
164+
165+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
166+ => HasPattern s (Maybe Text) where
167+ pattern = paramSchema.pattern
168+
169+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
170+ => HasMaxItems s (Maybe Integer) where
171+ maxItems = paramSchema.maxItems
172+
173+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
174+ => HasMinItems s (Maybe Integer) where
175+ minItems = paramSchema.minItems
176+
177+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
178+ => HasUniqueItems s (Maybe Bool) where
179+ uniqueItems = paramSchema.uniqueItems
180+
181+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
182+ => HasEnum s (Maybe [Value]) where
183+ enum_ = paramSchema.enum_
184+
185+instance {-# OVERLAPPABLE #-} HasParamSchema s (ParamSchema t)
186+ => HasMultipleOf s (Maybe Scientific) where
187+ multipleOf = paramSchema.multipleOf
src/Data/OpenApi/Operation.hs → src/Data/Swagger/Operation.hsrenamed+31−140
@@ -1,13 +1,13 @@
11{-# LANGUAGE RankNTypes #-}
22-- |
3−-- Module: Data.OpenApi.Operation
3+-- Module: Data.Swagger.Operation
44-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
55-- Stability: experimental
66--
77-- Helper traversals and functions for Swagger operations manipulations.
88-- These might be useful when you already have Swagger specification
99-- generated by something else.
10−module Data.OpenApi.Operation (
10+module Data.Swagger.Operation (
1111 -- * Operation traversals
1212 allOperations,
1313 operationsOf,
@@ -40,12 +40,11 @@ import Data.Maybe (mapMaybe)
4040import Data.Proxy
4141import qualified Data.Set as Set
4242import Data.Text (Text)
43−import Network.HTTP.Media (MediaType)
4443
45−import Data.OpenApi.Declare
46−import Data.OpenApi.Internal
47−import Data.OpenApi.Lens
48−import Data.OpenApi.Schema
44+import Data.Swagger.Declare
45+import Data.Swagger.Internal
46+import Data.Swagger.Lens
47+import Data.Swagger.Schema
4948
5049import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
5150import qualified Data.HashSet.InsOrd as InsOrdHS
@@ -54,18 +53,14 @@ import qualified Data.HashSet.InsOrd as InsOrdHS
5453-- >>> import Data.Aeson
5554-- >>> import Data.Proxy
5655-- >>> import Data.Time
57−-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
58−-- >>> import Data.OpenApi.Internal.Utils
5956
6057-- | Prepend path piece to all operations of the spec.
6158-- Leading and trailing slashes are trimmed/added automatically.
6259--
63−-- >>> let api = (mempty :: OpenApi) & paths .~ [("/info", mempty)]
64−-- >>> BSL.putStrLn $ encodePretty $ prependPath "user/{user_id}" api ^. paths
65−-- {
66−-- "/user/{user_id}/info": {}
67−-- }
68−prependPath :: FilePath -> OpenApi -> OpenApi
60+-- >>> let api = (mempty :: Swagger) & paths .~ [("/info", mempty)]
61+-- >>> encode $ prependPath "user/{user_id}" api ^. paths
62+-- "{\"/user/{user_id}/info\":{}}"
63+prependPath :: FilePath -> Swagger -> Swagger
6964prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
7065 where
7166 x </> y = case trim y of
@@ -75,7 +70,7 @@ prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
7570 trim = dropWhile (== '/') . dropWhileEnd (== '/')
7671
7772-- | All operations of a Swagger spec.
78−allOperations :: Traversal' OpenApi Operation
73+allOperations :: Traversal' Swagger Operation
7974allOperations = paths.traverse.template
8075
8176-- | @'operationsOf' sub@ will traverse only those operations
@@ -83,66 +78,13 @@ allOperations = paths.traverse.template
8378-- by both path and method.
8479--
8580-- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"
86−-- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
87−-- >>> let sub = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]
88−-- >>> BSL.putStrLn $ encodePretty api
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−-- }
115−-- >>> BSL.putStrLn $ encodePretty $ api & operationsOf sub . at 404 ?~ "Not found"
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−-- }
145−operationsOf :: OpenApi -> Traversal' OpenApi Operation
81+-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
82+-- >>> let sub = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
83+-- >>> encode api
84+-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
85+-- >>> encode $ 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\"}}}}}}"
87+operationsOf :: Swagger -> Traversal' Swagger Operation
14688operationsOf sub = paths.itraversed.withIndex.subops
14789 where
14890 -- | Traverse operations that correspond to paths and methods of the sub API.
@@ -164,12 +106,12 @@ operationsOf sub = paths.itraversed.withIndex.subops
164106-- @
165107-- 'applyTags' = 'applyTagsFor' 'allOperations'
166108-- @
167−applyTags :: [Tag] -> OpenApi -> OpenApi
109+applyTags :: [Tag] -> Swagger -> Swagger
168110applyTags = applyTagsFor allOperations
169111
170112-- | Apply tags to a part of Swagger spec and update the global
171113-- list of tags.
172−applyTagsFor :: Traversal' OpenApi Operation -> [Tag] -> OpenApi -> OpenApi
114+applyTagsFor :: Traversal' Swagger Operation -> [Tag] -> Swagger -> Swagger
173115applyTagsFor ops ts swag = swag
174116 & ops . tags %~ (<> InsOrdHS.fromList (map _tagName ts))
175117 & tags %~ (<> InsOrdHS.fromList ts)
@@ -179,27 +121,9 @@ applyTagsFor ops ts swag = swag
179121--
180122-- FIXME doc
181123--
182−-- >>> BSL.putStrLn $ encodePretty $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty
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−-- ]
202−declareResponse :: ToSchema a => MediaType -> Proxy a -> Declare (Definitions Schema) Response
124+-- >>> encode $ runDeclare (declareResponse (Proxy :: Proxy Day)) mempty
125+-- "[{\"Day\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}},{\"description\":\"\",\"schema\":{\"$ref\":\"#/definitions/Day\"}}]"
126+declareResponse :: ToSchema a => Text -> Proxy a -> Declare (Definitions Schema) Response
203127declareResponse cType proxy = do
204128 s <- declareSchemaRef proxy
205129 return (mempty & content.at cType ?~ (mempty & schema ?~ s))
@@ -215,46 +139,13 @@ declareResponse cType proxy = do
215139--
216140-- Example:
217141--
218−-- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]
219−-- >>> let res = declareResponse "application/json" (Proxy :: Proxy Day)
220−-- >>> BSL.putStrLn $ encodePretty $ api & setResponse 200 res
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−-- }
142+-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
143+-- >>> let res = declareResponse (Proxy :: Proxy Day)
144+-- >>> encode $ 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\"}}}"
255146--
256147-- See also @'setResponseWith'@.
257−setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
148+setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
258149setResponse = setResponseFor allOperations
259150
260151-- | Set or update response for all operations.
@@ -268,7 +159,7 @@ setResponse = setResponseFor allOperations
268159-- @
269160--
270161-- See also @'setResponse'@.
271−setResponseWith :: (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
162+setResponseWith :: (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
272163setResponseWith = setResponseForWith allOperations
273164
274165-- | Set response for specified operations.
@@ -277,7 +168,7 @@ setResponseWith = setResponseForWith allOperations
277168-- If the response already exists it will be overwritten.
278169--
279170-- See also @'setResponseForWith'@.
280−setResponseFor :: Traversal' OpenApi Operation -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
171+setResponseFor :: Traversal' Swagger Operation -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
281172setResponseFor ops code dres swag = swag
282173 & components.schemas %~ (<> defs)
283174 & ops . at code ?~ Inline res
@@ -291,7 +182,7 @@ setResponseFor ops code dres swag = swag
291182-- then just the new response is used.
292183--
293184-- See also @'setResponseFor'@.
294−setResponseForWith :: Traversal' OpenApi Operation -> (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
185+setResponseForWith :: Traversal' Swagger Operation -> (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
295186setResponseForWith ops f code dres swag = swag
296187 & components.schemas %~ (<> defs)
297188 & ops . at code %~ Just . Inline . combine
src/Data/Swagger/Optics.hsnew file+605
@@ -0,0 +1,605 @@
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 ''Host
84+makeFieldLabels ''Info
85+makeFieldLabels ''Contact
86+makeFieldLabels ''License
87+makeFieldLabels ''PathItem
88+makeFieldLabels ''Tag
89+makeFieldLabels ''Operation
90+makeFieldLabels ''Param
91+makeFieldLabels ''ParamOtherSchema
92+makeFieldLabels ''Header
93+makeFieldLabels ''Schema
94+makeFieldLabels ''NamedSchema
95+makeFieldLabels ''ParamSchema
96+makeFieldLabels ''Xml
97+makeFieldLabels ''Responses
98+makeFieldLabels ''Response
99+makeFieldLabels ''SecurityScheme
100+makeFieldLabels ''ApiKeyParams
101+makeFieldLabels ''OAuth2Params
102+makeFieldLabels ''ExternalDocs
103+makeFieldLabels ''Encoding
104+makeFieldLabels ''Example
105+makeFieldLabels ''Discriminator
106+
107+-- Prisms
108+
109+makePrismLabels ''ParamAnySchema
110+makePrismLabels ''SecuritySchemeType
111+makePrismLabels ''Referenced
112+
113+-- SwaggerItems prisms
114+
115+instance
116+ ( a ~ [Referenced Schema]
117+ , b ~ [Referenced Schema]
118+ ) => LabelOptic "_SwaggerItemsArray"
119+ A_Review
120+ (SwaggerItems 'SwaggerKindSchema)
121+ (SwaggerItems 'SwaggerKindSchema)
122+ a
123+ b where
124+ labelOptic = unto (\x -> SwaggerItemsArray x)
125+ {-# INLINE labelOptic #-}
126+
127+instance
128+ ( a ~ Referenced Schema
129+ , b ~ Referenced Schema
130+ ) => LabelOptic "_SwaggerItemsObject"
131+ A_Review
132+ (SwaggerItems 'SwaggerKindSchema)
133+ (SwaggerItems 'SwaggerKindSchema)
134+ a
135+ b where
136+ labelOptic = unto (\x -> SwaggerItemsObject x)
137+ {-# INLINE labelOptic #-}
138+
139+instance
140+ ( a ~ (Maybe (CollectionFormat t), ParamSchema t)
141+ , b ~ (Maybe (CollectionFormat t), ParamSchema t)
142+ ) => LabelOptic "_SwaggerItemsPrimitive"
143+ A_Review
144+ (SwaggerItems t)
145+ (SwaggerItems t)
146+ a
147+ b where
148+ labelOptic = unto (\(c, p) -> SwaggerItemsPrimitive c p)
149+ {-# INLINE labelOptic #-}
150+
151+-- =============================================================
152+-- More helpful instances for easier access to schema properties
153+
154+type instance Index Responses = HttpStatusCode
155+type instance Index Operation = HttpStatusCode
156+
157+type instance IxValue Responses = Referenced Response
158+type instance IxValue Operation = Referenced Response
159+
160+instance Ixed Responses where
161+ ix n = #responses % ix n
162+ {-# INLINE ix #-}
163+instance At Responses where
164+ at n = #responses % at n
165+ {-# INLINE at #-}
166+
167+instance Ixed Operation where
168+ ix n = #responses % ix n
169+ {-# INLINE ix #-}
170+instance At Operation where
171+ at n = #responses % at n
172+ {-# INLINE at #-}
173+
174+-- #paramSchema
175+
176+instance
177+ ( a ~ ParamSchema 'SwaggerKindSchema
178+ , b ~ ParamSchema 'SwaggerKindSchema
179+ ) => LabelOptic "paramSchema" A_Lens NamedSchema NamedSchema a b where
180+ labelOptic = #schema % #paramSchema
181+ {-# INLINE labelOptic #-}
182+
183+-- #type
184+
185+instance
186+ ( a ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
187+ , b ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
188+ ) => LabelOptic "type" A_Lens Header Header a b where
189+ labelOptic = #paramSchema % #type
190+ {-# INLINE labelOptic #-}
191+
192+instance
193+ ( a ~ Maybe (SwaggerType 'SwaggerKindSchema)
194+ , b ~ Maybe (SwaggerType 'SwaggerKindSchema)
195+ ) => LabelOptic "type" A_Lens Schema Schema a b where
196+ labelOptic = #paramSchema % #type
197+ {-# INLINE labelOptic #-}
198+
199+instance
200+ ( a ~ Maybe (SwaggerType 'SwaggerKindSchema)
201+ , b ~ Maybe (SwaggerType 'SwaggerKindSchema)
202+ ) => LabelOptic "type" A_Lens NamedSchema NamedSchema a b where
203+ labelOptic = #paramSchema % #type
204+ {-# INLINE labelOptic #-}
205+
206+instance
207+ ( a ~ Maybe (SwaggerType 'SwaggerKindParamOtherSchema)
208+ , b ~ Maybe (SwaggerType 'SwaggerKindParamOtherSchema)
209+ ) => LabelOptic "type" A_Lens ParamOtherSchema ParamOtherSchema a b where
210+ labelOptic = #paramSchema % #type
211+ {-# INLINE labelOptic #-}
212+
213+-- #default
214+
215+instance
216+ ( a ~ Maybe Value, b ~ Maybe Value
217+ ) => LabelOptic "default" A_Lens Header Header a b where
218+ labelOptic = #paramSchema % #default
219+ {-# INLINE labelOptic #-}
220+
221+instance
222+ ( a ~ Maybe Value, b ~ Maybe Value
223+ ) => LabelOptic "default" A_Lens Schema Schema a b where
224+ labelOptic = #paramSchema % #default
225+ {-# INLINE labelOptic #-}
226+
227+instance
228+ ( a ~ Maybe Value, b ~ Maybe Value
229+ ) => LabelOptic "default" A_Lens NamedSchema NamedSchema a b where
230+ labelOptic = #paramSchema % #default
231+ {-# INLINE labelOptic #-}
232+
233+instance
234+ ( a ~ Maybe Value, b ~ Maybe Value
235+ ) => LabelOptic "default" A_Lens ParamOtherSchema ParamOtherSchema a b where
236+ labelOptic = #paramSchema % #default
237+ {-# INLINE labelOptic #-}
238+
239+-- #format
240+
241+instance
242+ ( a ~ Maybe Format, b ~ Maybe Format
243+ ) => LabelOptic "format" A_Lens Header Header a b where
244+ labelOptic = #paramSchema % #format
245+ {-# INLINE labelOptic #-}
246+
247+instance
248+ ( a ~ Maybe Format, b ~ Maybe Format
249+ ) => LabelOptic "format" A_Lens Schema Schema a b where
250+ labelOptic = #paramSchema % #format
251+ {-# INLINE labelOptic #-}
252+
253+instance
254+ ( a ~ Maybe Format, b ~ Maybe Format
255+ ) => LabelOptic "format" A_Lens NamedSchema NamedSchema a b where
256+ labelOptic = #paramSchema % #format
257+ {-# INLINE labelOptic #-}
258+
259+instance
260+ ( a ~ Maybe Format, b ~ Maybe Format
261+ ) => LabelOptic "format" A_Lens ParamOtherSchema ParamOtherSchema a b where
262+ labelOptic = #paramSchema % #format
263+ {-# INLINE labelOptic #-}
264+
265+-- #items
266+
267+instance
268+ ( a ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
269+ , b ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
270+ ) => LabelOptic "items" A_Lens Header Header a b where
271+ labelOptic = #paramSchema % #items
272+ {-# INLINE labelOptic #-}
273+
274+instance
275+ ( a ~ Maybe (SwaggerItems 'SwaggerKindSchema)
276+ , b ~ Maybe (SwaggerItems 'SwaggerKindSchema)
277+ ) => LabelOptic "items" A_Lens Schema Schema a b where
278+ labelOptic = #paramSchema % #items
279+ {-# INLINE labelOptic #-}
280+
281+instance
282+ ( a ~ Maybe (SwaggerItems 'SwaggerKindSchema)
283+ , b ~ Maybe (SwaggerItems 'SwaggerKindSchema)
284+ ) => LabelOptic "items" A_Lens NamedSchema NamedSchema a b where
285+ labelOptic = #paramSchema % #items
286+ {-# INLINE labelOptic #-}
287+
288+instance
289+ ( a ~ Maybe (SwaggerItems 'SwaggerKindParamOtherSchema)
290+ , b ~ Maybe (SwaggerItems 'SwaggerKindParamOtherSchema)
291+ ) => LabelOptic "items" A_Lens ParamOtherSchema ParamOtherSchema a b where
292+ labelOptic = #paramSchema % #items
293+ {-# INLINE labelOptic #-}
294+
295+-- #maximum
296+
297+instance
298+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
299+ ) => LabelOptic "maximum" A_Lens Header Header a b where
300+ labelOptic = #paramSchema % #maximum
301+ {-# INLINE labelOptic #-}
302+
303+instance
304+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
305+ ) => LabelOptic "maximum" A_Lens Schema Schema a b where
306+ labelOptic = #paramSchema % #maximum
307+ {-# INLINE labelOptic #-}
308+
309+instance
310+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
311+ ) => LabelOptic "maximum" A_Lens NamedSchema NamedSchema a b where
312+ labelOptic = #paramSchema % #maximum
313+ {-# INLINE labelOptic #-}
314+
315+instance
316+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
317+ ) => LabelOptic "maximum" A_Lens ParamOtherSchema ParamOtherSchema a b where
318+ labelOptic = #paramSchema % #maximum
319+ {-# INLINE labelOptic #-}
320+
321+-- #exclusiveMaximum
322+
323+instance
324+ ( a ~ Maybe Bool, b ~ Maybe Bool
325+ ) => LabelOptic "exclusiveMaximum" A_Lens Header Header a b where
326+ labelOptic = #paramSchema % #exclusiveMaximum
327+ {-# INLINE labelOptic #-}
328+
329+instance
330+ ( a ~ Maybe Bool, b ~ Maybe Bool
331+ ) => LabelOptic "exclusiveMaximum" A_Lens Schema Schema a b where
332+ labelOptic = #paramSchema % #exclusiveMaximum
333+ {-# INLINE labelOptic #-}
334+
335+instance
336+ ( a ~ Maybe Bool, b ~ Maybe Bool
337+ ) => LabelOptic "exclusiveMaximum" A_Lens NamedSchema NamedSchema a b where
338+ labelOptic = #paramSchema % #exclusiveMaximum
339+ {-# INLINE labelOptic #-}
340+
341+instance
342+ ( a ~ Maybe Bool, b ~ Maybe Bool
343+ ) => LabelOptic "exclusiveMaximum" A_Lens ParamOtherSchema ParamOtherSchema a b where
344+ labelOptic = #paramSchema % #exclusiveMaximum
345+ {-# INLINE labelOptic #-}
346+
347+-- #minimum
348+
349+instance
350+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
351+ ) => LabelOptic "minimum" A_Lens Header Header a b where
352+ labelOptic = #paramSchema % #minimum
353+ {-# INLINE labelOptic #-}
354+
355+instance
356+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
357+ ) => LabelOptic "minimum" A_Lens Schema Schema a b where
358+ labelOptic = #paramSchema % #minimum
359+ {-# INLINE labelOptic #-}
360+
361+instance
362+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
363+ ) => LabelOptic "minimum" A_Lens NamedSchema NamedSchema a b where
364+ labelOptic = #paramSchema % #minimum
365+ {-# INLINE labelOptic #-}
366+
367+instance
368+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
369+ ) => LabelOptic "minimum" A_Lens ParamOtherSchema ParamOtherSchema a b where
370+ labelOptic = #paramSchema % #minimum
371+ {-# INLINE labelOptic #-}
372+
373+-- #exclusiveMinimum
374+
375+instance
376+ ( a ~ Maybe Bool, b ~ Maybe Bool
377+ ) => LabelOptic "exclusiveMinimum" A_Lens Header Header a b where
378+ labelOptic = #paramSchema % #exclusiveMinimum
379+ {-# INLINE labelOptic #-}
380+
381+instance
382+ ( a ~ Maybe Bool, b ~ Maybe Bool
383+ ) => LabelOptic "exclusiveMinimum" A_Lens Schema Schema a b where
384+ labelOptic = #paramSchema % #exclusiveMinimum
385+ {-# INLINE labelOptic #-}
386+
387+instance
388+ ( a ~ Maybe Bool, b ~ Maybe Bool
389+ ) => LabelOptic "exclusiveMinimum" A_Lens NamedSchema NamedSchema a b where
390+ labelOptic = #paramSchema % #exclusiveMinimum
391+ {-# INLINE labelOptic #-}
392+
393+instance
394+ ( a ~ Maybe Bool, b ~ Maybe Bool
395+ ) => LabelOptic "exclusiveMinimum" A_Lens ParamOtherSchema ParamOtherSchema a b where
396+ labelOptic = #paramSchema % #exclusiveMinimum
397+ {-# INLINE labelOptic #-}
398+
399+-- #maxLength
400+
401+instance
402+ ( a ~ Maybe Integer, b ~ Maybe Integer
403+ ) => LabelOptic "maxLength" A_Lens Header Header a b where
404+ labelOptic = #paramSchema % #maxLength
405+ {-# INLINE labelOptic #-}
406+
407+instance
408+ ( a ~ Maybe Integer, b ~ Maybe Integer
409+ ) => LabelOptic "maxLength" A_Lens Schema Schema a b where
410+ labelOptic = #paramSchema % #maxLength
411+ {-# INLINE labelOptic #-}
412+
413+instance
414+ ( a ~ Maybe Integer, b ~ Maybe Integer
415+ ) => LabelOptic "maxLength" A_Lens NamedSchema NamedSchema a b where
416+ labelOptic = #paramSchema % #maxLength
417+ {-# INLINE labelOptic #-}
418+
419+instance
420+ ( a ~ Maybe Integer, b ~ Maybe Integer
421+ ) => LabelOptic "maxLength" A_Lens ParamOtherSchema ParamOtherSchema a b where
422+ labelOptic = #paramSchema % #maxLength
423+ {-# INLINE labelOptic #-}
424+
425+-- #minLength
426+
427+instance
428+ ( a ~ Maybe Integer, b ~ Maybe Integer
429+ ) => LabelOptic "minLength" A_Lens Header Header a b where
430+ labelOptic = #paramSchema % #minLength
431+ {-# INLINE labelOptic #-}
432+
433+instance
434+ ( a ~ Maybe Integer, b ~ Maybe Integer
435+ ) => LabelOptic "minLength" A_Lens Schema Schema a b where
436+ labelOptic = #paramSchema % #minLength
437+ {-# INLINE labelOptic #-}
438+
439+instance
440+ ( a ~ Maybe Integer, b ~ Maybe Integer
441+ ) => LabelOptic "minLength" A_Lens NamedSchema NamedSchema a b where
442+ labelOptic = #paramSchema % #minLength
443+ {-# INLINE labelOptic #-}
444+
445+instance
446+ ( a ~ Maybe Integer, b ~ Maybe Integer
447+ ) => LabelOptic "minLength" A_Lens ParamOtherSchema ParamOtherSchema a b where
448+ labelOptic = #paramSchema % #minLength
449+ {-# INLINE labelOptic #-}
450+
451+-- #pattern
452+
453+instance
454+ ( a ~ Maybe Text, b ~ Maybe Text
455+ ) => LabelOptic "pattern" A_Lens Header Header a b where
456+ labelOptic = #paramSchema % #pattern
457+ {-# INLINE labelOptic #-}
458+
459+instance
460+ ( a ~ Maybe Text, b ~ Maybe Text
461+ ) => LabelOptic "pattern" A_Lens Schema Schema a b where
462+ labelOptic = #paramSchema % #pattern
463+ {-# INLINE labelOptic #-}
464+
465+instance
466+ ( a ~ Maybe Text, b ~ Maybe Text
467+ ) => LabelOptic "pattern" A_Lens NamedSchema NamedSchema a b where
468+ labelOptic = #paramSchema % #pattern
469+ {-# INLINE labelOptic #-}
470+
471+instance
472+ ( a ~ Maybe Text, b ~ Maybe Text
473+ ) => LabelOptic "pattern" A_Lens ParamOtherSchema ParamOtherSchema a b where
474+ labelOptic = #paramSchema % #pattern
475+ {-# INLINE labelOptic #-}
476+
477+-- #maxItems
478+
479+instance
480+ ( a ~ Maybe Integer, b ~ Maybe Integer
481+ ) => LabelOptic "maxItems" A_Lens Header Header a b where
482+ labelOptic = #paramSchema % #maxItems
483+ {-# INLINE labelOptic #-}
484+
485+instance
486+ ( a ~ Maybe Integer, b ~ Maybe Integer
487+ ) => LabelOptic "maxItems" A_Lens Schema Schema a b where
488+ labelOptic = #paramSchema % #maxItems
489+ {-# INLINE labelOptic #-}
490+
491+instance
492+ ( a ~ Maybe Integer, b ~ Maybe Integer
493+ ) => LabelOptic "maxItems" A_Lens NamedSchema NamedSchema a b where
494+ labelOptic = #paramSchema % #maxItems
495+ {-# INLINE labelOptic #-}
496+
497+instance
498+ ( a ~ Maybe Integer, b ~ Maybe Integer
499+ ) => LabelOptic "maxItems" A_Lens ParamOtherSchema ParamOtherSchema a b where
500+ labelOptic = #paramSchema % #maxItems
501+ {-# INLINE labelOptic #-}
502+
503+-- #minItems
504+
505+instance
506+ ( a ~ Maybe Integer, b ~ Maybe Integer
507+ ) => LabelOptic "minItems" A_Lens Header Header a b where
508+ labelOptic = #paramSchema % #minItems
509+ {-# INLINE labelOptic #-}
510+
511+instance
512+ ( a ~ Maybe Integer, b ~ Maybe Integer
513+ ) => LabelOptic "minItems" A_Lens Schema Schema a b where
514+ labelOptic = #paramSchema % #minItems
515+ {-# INLINE labelOptic #-}
516+
517+instance
518+ ( a ~ Maybe Integer, b ~ Maybe Integer
519+ ) => LabelOptic "minItems" A_Lens NamedSchema NamedSchema a b where
520+ labelOptic = #paramSchema % #minItems
521+ {-# INLINE labelOptic #-}
522+
523+instance
524+ ( a ~ Maybe Integer, b ~ Maybe Integer
525+ ) => LabelOptic "minItems" A_Lens ParamOtherSchema ParamOtherSchema a b where
526+ labelOptic = #paramSchema % #minItems
527+ {-# INLINE labelOptic #-}
528+
529+-- #uniqueItems
530+
531+instance
532+ ( a ~ Maybe Bool, b ~ Maybe Bool
533+ ) => LabelOptic "uniqueItems" A_Lens Header Header a b where
534+ labelOptic = #paramSchema % #uniqueItems
535+ {-# INLINE labelOptic #-}
536+
537+instance
538+ ( a ~ Maybe Bool, b ~ Maybe Bool
539+ ) => LabelOptic "uniqueItems" A_Lens Schema Schema a b where
540+ labelOptic = #paramSchema % #uniqueItems
541+ {-# INLINE labelOptic #-}
542+
543+instance
544+ ( a ~ Maybe Bool, b ~ Maybe Bool
545+ ) => LabelOptic "uniqueItems" A_Lens NamedSchema NamedSchema a b where
546+ labelOptic = #paramSchema % #uniqueItems
547+ {-# INLINE labelOptic #-}
548+
549+instance
550+ ( a ~ Maybe Bool, b ~ Maybe Bool
551+ ) => LabelOptic "uniqueItems" A_Lens ParamOtherSchema ParamOtherSchema a b where
552+ labelOptic = #paramSchema % #uniqueItems
553+ {-# INLINE labelOptic #-}
554+
555+-- #enum
556+
557+instance
558+ ( a ~ Maybe [Value], b ~ Maybe [Value]
559+ ) => LabelOptic "enum" A_Lens Header Header a b where
560+ labelOptic = #paramSchema % #enum
561+ {-# INLINE labelOptic #-}
562+
563+instance
564+ ( a ~ Maybe [Value], b ~ Maybe [Value]
565+ ) => LabelOptic "enum" A_Lens Schema Schema a b where
566+ labelOptic = #paramSchema % #enum
567+ {-# INLINE labelOptic #-}
568+
569+instance
570+ ( a ~ Maybe [Value], b ~ Maybe [Value]
571+ ) => LabelOptic "enum" A_Lens NamedSchema NamedSchema a b where
572+ labelOptic = #paramSchema % #enum
573+ {-# INLINE labelOptic #-}
574+
575+instance
576+ ( a ~ Maybe [Value], b ~ Maybe [Value]
577+ ) => LabelOptic "enum" A_Lens ParamOtherSchema ParamOtherSchema a b where
578+ labelOptic = #paramSchema % #enum
579+ {-# INLINE labelOptic #-}
580+
581+-- #multipleOf
582+
583+instance
584+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
585+ ) => LabelOptic "multipleOf" A_Lens Header Header a b where
586+ labelOptic = #paramSchema % #multipleOf
587+ {-# INLINE labelOptic #-}
588+
589+instance
590+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
591+ ) => LabelOptic "multipleOf" A_Lens Schema Schema a b where
592+ labelOptic = #paramSchema % #multipleOf
593+ {-# INLINE labelOptic #-}
594+
595+instance
596+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
597+ ) => LabelOptic "multipleOf" A_Lens NamedSchema NamedSchema a b where
598+ labelOptic = #paramSchema % #multipleOf
599+ {-# INLINE labelOptic #-}
600+
601+instance
602+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
603+ ) => LabelOptic "multipleOf" A_Lens ParamOtherSchema ParamOtherSchema a b where
604+ labelOptic = #paramSchema % #multipleOf
605+ {-# INLINE labelOptic #-}
src/Data/OpenApi/ParamSchema.hs → src/Data/Swagger/ParamSchema.hsrenamed+7−7
@@ -1,10 +1,10 @@
11-- |
2−-- Module: Data.OpenApi.ParamSchema
2+-- Module: Data.Swagger.ParamSchema
33-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
44-- Stability: experimental
55--
66-- Types and functions for working with Swagger parameter schema.
7−module Data.OpenApi.ParamSchema (
7+module Data.Swagger.ParamSchema (
88 -- * Encoding
99 ToParamSchema(..),
1010
@@ -13,14 +13,14 @@ module Data.OpenApi.ParamSchema (
1313 toParamSchemaBoundedIntegral,
1414
1515 -- * Schema templates
16− passwordSchema,
17− binarySchema,
18− byteSchema,
16+ passwordParamSchema,
17+ binaryParamSchema,
18+ byteParamSchema,
1919
2020 -- * Generic encoding configuration
2121 SchemaOptions(..),
2222 defaultSchemaOptions,
2323) where
2424
25−import Data.OpenApi.Internal.ParamSchema
26−import Data.OpenApi.SchemaOptions
25+import Data.Swagger.Internal.ParamSchema
26+import Data.Swagger.SchemaOptions
src/Data/OpenApi/Schema.hs → src/Data/Swagger/Schema.hsrenamed+13−4
@@ -1,10 +1,10 @@
11-- |
2−-- Module: Data.OpenApi.Schema
2+-- Module: Data.Swagger.Schema
33-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
44-- Stability: experimental
55--
66-- Types and functions for working with Swagger schema.
7−module Data.OpenApi.Schema (
7+module Data.Swagger.Schema (
88 -- * Encoding
99 ToSchema(..),
1010 declareSchema,
@@ -32,6 +32,15 @@ module Data.OpenApi.Schema (
3232 paramSchemaToNamedSchema,
3333 paramSchemaToSchema,
3434
35+ -- ** Unrestricted versions
36+ genericDeclareNamedSchemaUnrestricted,
37+ genericDeclareSchemaUnrestricted,
38+
39+ -- * Schema templates
40+ passwordSchema,
41+ binarySchema,
42+ byteSchema,
43+
3544 -- * Sketching @'Schema'@s using @'ToJSON'@
3645 sketchSchema,
3746 sketchStrictSchema,
@@ -48,5 +57,5 @@ module Data.OpenApi.Schema (
4857 fromAesonOptions,
4958) where
5059
51−import Data.OpenApi.Internal.Schema
52−import Data.OpenApi.SchemaOptions
60+import Data.Swagger.Internal.Schema
61+import Data.Swagger.SchemaOptions
src/Data/OpenApi/Schema/Generator.hs → src/Data/Swagger/Schema/Generator.hsrenamed+19−23
@@ -2,7 +2,7 @@
22{-# LANGUAGE OverloadedLists #-}
33{-# LANGUAGE ScopedTypeVariables #-}
44
5−module Data.OpenApi.Schema.Generator where
5+module Data.Swagger.Schema.Generator where
66
77import Prelude ()
88import Prelude.Compat
@@ -16,24 +16,20 @@ import Data.Maybe
1616import Data.Proxy
1717import Data.Scientific
1818import qualified Data.Set as S
19−import Data.OpenApi
20−import Data.OpenApi.Declare
21−import Data.OpenApi.Internal.Schema.Validation (inferSchemaTypes)
19+import Data.Swagger
20+import Data.Swagger.Declare
21+import Data.Swagger.Internal.Schema.Validation (inferSchemaTypes)
2222import qualified Data.Text as T
2323import qualified Data.Vector as V
2424import Test.QuickCheck (arbitrary)
2525import Test.QuickCheck.Gen
2626import Test.QuickCheck.Property
2727
28−import Data.OpenApi.Aeson.Compat (fromInsOrdHashMap)
29−
3028-- | Note: 'schemaGen' may 'error', if schema type is not specified,
3129-- and cannot be inferred.
3230schemaGen :: Definitions Schema -> Schema -> Gen Value
3331schemaGen _ schema
34− | Just cases <- schema ^. enum_ = elements cases
35−schemaGen defns schema
36− | Just variants <- schema ^. oneOf = schemaGen defns =<< elements (dereference defns <$> variants)
32+ | Just cases <- schema ^. paramSchema . enum_ = elements cases
3733schemaGen defns schema =
3834 case schema ^. type_ of
3935 Nothing ->
@@ -41,25 +37,25 @@ schemaGen defns schema =
4137 [ inferredType ] -> schemaGen defns (schema & type_ ?~ inferredType)
4238 -- Gen is not MonadFail
4339 _ -> error "unable to infer schema type"
44− Just OpenApiBoolean -> Bool <$> elements [True, False]
45− Just OpenApiNull -> pure Null
46− Just OpenApiNumber
40+ Just SwaggerBoolean -> Bool <$> elements [True, False]
41+ Just SwaggerNull -> pure Null
42+ Just SwaggerNumber
4743 | Just min <- schema ^. minimum_
4844 , Just max <- schema ^. maximum_ ->
4945 Number . fromFloatDigits <$>
5046 choose (toRealFloat min, toRealFloat max :: Double)
5147 | otherwise -> Number .fromFloatDigits <$> (arbitrary :: Gen Double)
52− Just OpenApiInteger
48+ Just SwaggerInteger
5349 | Just min <- schema ^. minimum_
5450 , Just max <- schema ^. maximum_ ->
5551 Number . fromInteger <$>
5652 choose (truncate min, truncate max)
5753 | otherwise -> Number . fromInteger <$> arbitrary
58− Just OpenApiArray
54+ Just SwaggerArray
5955 | Just 0 <- schema ^. maxLength -> pure $ Array V.empty
6056 | Just items <- schema ^. items ->
6157 case items of
62− OpenApiItemsObject ref -> do
58+ SwaggerItemsObject ref -> do
6359 size <- getSize
6460 let itemSchema = dereference defns ref
6561 minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minItems
@@ -67,17 +63,17 @@ schemaGen defns schema =
6763 arrayLength <- choose (minLength', max minLength' maxLength')
6864 generatedArray <- vectorOf arrayLength $ schemaGen defns itemSchema
6965 return . Array $ V.fromList generatedArray
70− OpenApiItemsArray refs ->
66+ SwaggerItemsArray refs ->
7167 let itemGens = schemaGen defns . dereference defns <$> refs
7268 in fmap (Array . V.fromList) $ sequence itemGens
73− Just OpenApiString -> do
69+ Just SwaggerString -> do
7470 size <- getSize
7571 let minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minLength
7672 let maxLength' = fromMaybe size $ fromInteger <$> schema ^. maxLength
7773 length <- choose (minLength', max minLength' maxLength')
7874 str <- vectorOf length arbitrary
7975 return . String $ T.pack str
80− Just OpenApiObject -> do
76+ Just SwaggerObject -> do
8177 size <- getSize
8278 let props = dereference defns <$> schema ^. properties
8379 reqKeys = S.fromList $ schema ^. required
@@ -97,11 +93,11 @@ schemaGen defns schema =
9793 return . M.fromList $ zip additionalKeys (repeat . schemaGen defns $ dereference defns addlSchema)
9894 _ -> return []
9995 x <- sequence $ gens <> additionalGens
100− return . Object $ fromInsOrdHashMap x
101−
102−dereference :: Definitions a -> Referenced a -> a
103−dereference _ (Inline a) = a
104−dereference defs (Ref (Reference ref)) = fromJust $ M.lookup ref defs
96+ return . Object $ M.toHashMap x
97+ where
98+ dereference :: Definitions a -> Referenced a -> a
99+ dereference _ (Inline a) = a
100+ dereference defs (Ref (Reference ref)) = fromJust $ M.lookup ref defs
105101
106102genValue :: (ToSchema a) => Proxy a -> Gen Value
107103genValue p =
src/Data/OpenApi/Schema/Validation.hs → src/Data/Swagger/Schema/Validation.hsrenamed+7−7
@@ -1,10 +1,10 @@
11-- |
2−-- Module: Data.OpenApi.Schema.Validation
2+-- Module: Data.Swagger.Schema.Validation
33-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
44-- Stability: experimental
55--
66-- Validate JSON values with Swagger Schema.
7−module Data.OpenApi.Schema.Validation (
7+module Data.Swagger.Schema.Validation (
88 -- * How to use validation
99 -- $howto
1010
@@ -29,13 +29,13 @@ module Data.OpenApi.Schema.Validation (
2929 validateJSONWithPatternChecker,
3030) where
3131
32−import Data.OpenApi.Internal.Schema.Validation
32+import Data.Swagger.Internal.Schema.Validation
3333
3434-- $setup
3535-- >>> import Control.Lens
3636-- >>> import Data.Aeson
3737-- >>> import Data.Proxy
38−-- >>> import Data.OpenApi
38+-- >>> import Data.Swagger
3939-- >>> import GHC.Generics
4040-- >>> :set -XDeriveGeneric
4141
@@ -71,11 +71,11 @@ import Data.OpenApi.Internal.Schema.Validation
7171-- generally fails for @null@ JSON:
7272--
7373-- >>> validateToJSON (Nothing :: Maybe String)
74−-- ["expected JSON value of type OpenApiString"]
74+-- ["expected JSON value of type SwaggerString"]
7575-- >>> validateToJSON ([Just "hello", Nothing] :: [Maybe String])
76−-- ["expected JSON value of type OpenApiString"]
76+-- ["expected JSON value of type SwaggerString"]
7777-- >>> validateToJSON (123, Nothing :: Maybe String)
78−-- ["expected JSON value of type OpenApiString"]
78+-- ["expected JSON value of type SwaggerString"]
7979--
8080-- However, when @'Maybe' a@ is a type of a record field,
8181-- validation takes @'required'@ property of the @'Schema'@
src/Data/OpenApi/SchemaOptions.hs → src/Data/Swagger/SchemaOptions.hsrenamed+3−7
@@ -1,11 +1,11 @@
11{-# LANGUAGE RecordWildCards #-}
22-- |
3−-- Module: Data.OpenApi.SchemaOptions
3+-- Module: Data.Swagger.SchemaOptions
44-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
55-- Stability: experimental
66--
77-- Generic deriving options for @'ToParamSchema'@ and @'ToSchema'@.
8−module Data.OpenApi.SchemaOptions where
8+module Data.Swagger.SchemaOptions where
99
1010import qualified Data.Aeson.Types as Aeson
1111
@@ -22,8 +22,6 @@ data SchemaOptions = SchemaOptions
2222 , allNullaryToStringTag :: Bool
2323 -- | Hide the field name when a record constructor has only one field, like a newtype.
2424 , unwrapUnaryRecords :: Bool
25− -- | Specifies how to encode constructors of a sum datatype.
26− , sumEncoding :: Aeson.SumEncoding
2725 }
2826
2927-- | Default encoding @'SchemaOptions'@.
@@ -35,7 +33,6 @@ data SchemaOptions = SchemaOptions
3533-- , 'datatypeNameModifier' = id
3634-- , 'allNullaryToStringTag' = True
3735-- , 'unwrapUnaryRecords' = False
38−-- , 'sumEncoding' = 'Aeson.defaultTaggedObject'
3936-- }
4037-- @
4138defaultSchemaOptions :: SchemaOptions
@@ -45,7 +42,6 @@ defaultSchemaOptions = SchemaOptions
4542 , datatypeNameModifier = id
4643 , allNullaryToStringTag = True
4744 , unwrapUnaryRecords = False
48− , sumEncoding = Aeson.defaultTaggedObject
4945 }
5046
5147-- | Convert 'Aeson.Options' to 'SchemaOptions'.
@@ -60,6 +56,7 @@ defaultSchemaOptions = SchemaOptions
6056-- Note that these fields have no effect on `SchemaOptions`:
6157--
6258-- * 'Aeson.omitNothingFields'
59+-- * 'Aeson.sumEncoding'
6360-- * 'Aeson.tagSingleConstructors'
6461--
6562-- The rest is defined as in 'defaultSchemaOptions'.
@@ -72,5 +69,4 @@ fromAesonOptions opts = defaultSchemaOptions
7269 , constructorTagModifier = Aeson.constructorTagModifier opts
7370 , allNullaryToStringTag = Aeson.allNullaryToStringTag opts
7471 , unwrapUnaryRecords = Aeson.unwrapUnaryRecords opts
75− , sumEncoding = Aeson.sumEncoding opts
7672 }
stack.yaml+1−1
@@ -1,4 +1,4 @@
1−resolver: lts-16.31
1+resolver: lts-15.1
22packages:
33- '.'
44extra-deps:
openapi3.cabal → swagger2.cabalrenamed+57−65
@@ -1,34 +1,34 @@
11cabal-version: >=1.10
2−name: openapi3
3−version: 3.2.2
2+name: swagger2
3+version: 2.6.1
44
5−synopsis: OpenAPI 3.0 data model
6−category: Web, Swagger, OpenApi
5+synopsis: Swagger 2.0 data model
6+category: Web, Swagger
77description:
8− This library is intended to be used for decoding and encoding OpenAPI 3.0 API
8+ This library is intended to be used for decoding and encoding Swagger 2.0 API
99 specifications as well as manipulating them.
1010 .
11− The original OpenAPI 3.0 specification is available at http://swagger.io/specification/.
11+ The original Swagger 2.0 specification is available at http://swagger.io/specification/.
1212
13−homepage: https://github.com/biocad/openapi3
14−bug-reports: https://github.com/biocad/openapi3/issues
13+homepage: https://github.com/GetShopTV/swagger2
14+bug-reports: https://github.com/GetShopTV/swagger2/issues
1515license: BSD3
1616license-file: LICENSE
17−author: Nickolay Kudasov, Maxim Koltsov
18−maintainer: nickolay@getshoptv.com, kolmax94@gmail.com
19−copyright: (c) 2015-2016, GetShopTV, (c) 2020, Biocad
17+author: Nickolay Kudasov
18+maintainer: nickolay@getshoptv.com
19+copyright: (c) 2015-2016, GetShopTV
2020build-type: Custom
2121extra-source-files:
2222 README.md
2323 , CHANGELOG.md
2424 , examples/*.hs
2525tested-with:
26− GHC ==8.4.4
26+ GHC ==8.0.2
27+ || ==8.2.2
28+ || ==8.4.4
2729 || ==8.6.5
28− || ==8.8.4
29− || ==8.10.7
30− || ==9.0.2
31− || ==9.2.2
30+ || ==8.8.1
31+ || ==8.10.1
3232
3333custom-setup
3434 setup-depends:
@@ -37,56 +37,54 @@ custom-setup
3737library
3838 hs-source-dirs: src
3939 exposed-modules:
40− Data.OpenApi
41− Data.OpenApi.Declare
42− Data.OpenApi.Lens
43− Data.OpenApi.Operation
44− Data.OpenApi.Optics
45− Data.OpenApi.ParamSchema
46− Data.OpenApi.Schema
47− Data.OpenApi.Schema.Generator
48− Data.OpenApi.Schema.Validation
49− Data.OpenApi.SchemaOptions
40+ Data.Swagger
41+ Data.Swagger.Declare
42+ Data.Swagger.Lens
43+ Data.Swagger.Operation
44+ Data.Swagger.Optics
45+ Data.Swagger.ParamSchema
46+ Data.Swagger.Schema
47+ Data.Swagger.Schema.Generator
48+ Data.Swagger.Schema.Validation
49+ Data.Swagger.SchemaOptions
5050
5151 -- internal modules
52− Data.OpenApi.Internal
53− Data.OpenApi.Internal.Schema
54− Data.OpenApi.Internal.Schema.Validation
55− Data.OpenApi.Internal.ParamSchema
56− Data.OpenApi.Internal.Utils
57− Data.OpenApi.Internal.AesonUtils
58− Data.OpenApi.Internal.TypeShape
59−
60− Data.OpenApi.Aeson.Compat
52+ Data.Swagger.Internal
53+ Data.Swagger.Internal.Schema
54+ Data.Swagger.Internal.Schema.Validation
55+ Data.Swagger.Internal.ParamSchema
56+ Data.Swagger.Internal.Utils
57+ Data.Swagger.Internal.AesonUtils
58+ Data.Swagger.Internal.TypeShape
6159
6260 -- GHC boot libraries
6361 build-depends:
64− base >=4.11.1.0 && <4.17
65− , bytestring >=0.10.8.2 && <0.12
66− , containers >=0.5.11.0 && <0.7
67− , template-haskell >=2.13.0.0 && <2.19
68− , time >=1.8.0.2 && <1.14
69− , transformers >=0.5.5.0 && <0.6
62+ base >=4.9 && <4.15
63+ , bytestring >=0.10.8.1 && <0.11
64+ , containers >=0.5.7.1 && <0.7
65+ , template-haskell >=2.11.1.0 && <2.17
66+ , time >=1.6.0.1 && <1.10
67+ , transformers >=0.5.2.0 && <0.6
7068
7169 build-depends:
7270 mtl >=2.2.2 && <2.3
73− , text >=1.2.3.1 && <2.1
71+ , text >=1.2.3.0 && <1.3
7472
7573 -- other dependencies
7674 build-depends:
77− base-compat-batteries >=0.11.1 && <0.13
78− , aeson >=1.4.2.0 && <1.6 || >=2.0.1.0 && < 2.1
75+ base-compat-batteries >=0.11.1 && <0.12
76+ , aeson >=1.4.2.0 && <1.6
7977 , aeson-pretty >=0.8.7 && <0.9
8078 -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint
8179 , cookie >=0.4.3 && <0.5
8280 , generics-sop >=0.5.1.0 && <0.6
83− , hashable >=1.2.7.0 && <1.5
81+ , hashable >=1.2.7.0 && <1.4
8482 , http-media >=0.8.0.0 && <0.9
8583 , insert-ordered-containers >=0.2.3 && <0.3
86− , lens >=4.16.1 && <5.2
84+ , lens >=4.16.1 && <4.20
8785 , network >=2.6.3.5 && <3.2
88− , optics-core >=0.2 && <0.5
89− , optics-th >=0.2 && <0.5
86+ , optics-core >=0.2 && <0.4
87+ , optics-th >=0.2 && <0.4
9088 , scientific >=0.3.6.2 && <0.4
9189 , unordered-containers >=0.2.9.0 && <0.3
9290 , uuid-types >=1.0.3 && <1.1
@@ -104,7 +102,7 @@ test-suite spec
104102 -- We need aeson's toEncoding for doctests too
105103 build-depends:
106104 base
107− , QuickCheck
105+ , swagger2
108106 , aeson
109107 , base-compat-batteries
110108 , bytestring
@@ -113,32 +111,32 @@ test-suite spec
113111 , insert-ordered-containers
114112 , lens
115113 , mtl
116− , openapi3
117114 , template-haskell
118115 , text
119116 , time
120117 , unordered-containers
121118 , vector
119+ , QuickCheck
122120
123121 -- test-suite only dependencies
124122 build-depends:
125− hspec >=2.5.5 && <2.10
123+ hspec >=2.5.5 && <2.8
126124 , HUnit >=1.6.0.0 && <1.7
127125 , quickcheck-instances >=0.3.19 && <0.14
128126 , utf8-string >=1.0.1.1 && <1.1
129127
130128 -- https://github.com/haskell/cabal/issues/3708
131129 build-tool-depends:
132− hspec-discover:hspec-discover >=2.5.5 && <2.10
130+ hspec-discover:hspec-discover >=2.5.5 && <2.8
133131
134132 other-modules:
135133 SpecCommon
136− Data.OpenApiSpec
137− Data.OpenApi.CommonTestTypes
138− Data.OpenApi.ParamSchemaSpec
139− Data.OpenApi.SchemaSpec
140− Data.OpenApi.Schema.ValidationSpec
141− Data.OpenApi.Schema.GeneratorSpec
134+ Data.SwaggerSpec
135+ Data.Swagger.CommonTestTypes
136+ Data.Swagger.ParamSchemaSpec
137+ Data.Swagger.SchemaSpec
138+ Data.Swagger.Schema.ValidationSpec
139+ Data.Swagger.Schema.GeneratorSpec
142140 default-language: Haskell2010
143141
144142test-suite doctests
@@ -149,12 +147,6 @@ test-suite doctests
149147 main-is: doctests.hs
150148 type: exitcode-stdio-1.0
151149
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−
158150source-repository head
159151 type: git
160− location: https://github.com/biocad/openapi3.git
152+ location: https://github.com/GetShopTV/swagger2.git
test/Data/OpenApiSpec.hsdeleted−962
@@ -1,962 +0,0 @@
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/OpenApi/CommonTestTypes.hs → test/Data/Swagger/CommonTestTypes.hsrenamed+100−436
@@ -2,7 +2,7 @@
22{-# LANGUAGE QuasiQuotes #-}
33{-# LANGUAGE ScopedTypeVariables #-}
44
5−module Data.OpenApi.CommonTestTypes where
5+module Data.Swagger.CommonTestTypes where
66
77import Prelude ()
88import Prelude.Compat
@@ -18,7 +18,7 @@ import qualified Data.Text as Text
1818import Data.Word
1919import GHC.Generics
2020
21−import Data.OpenApi
21+import Data.Swagger
2222
2323-- ========================================================================
2424-- Unit type
@@ -88,7 +88,7 @@ paintSchemaJSON = [aesonQQ|
8888 {
8989 "color":
9090 {
91− "$ref": "#/components/schemas/Color"
91+ "$ref": "#/definitions/Color"
9292 }
9393 },
9494 "required": ["color"]
@@ -181,7 +181,7 @@ userGroupSchemaJSON :: Value
181181userGroupSchemaJSON = [aesonQQ|
182182{
183183 "type": "array",
184− "items": { "$ref": "#/components/schemas/UserId" },
184+ "items": { "$ref": "#/definitions/UserId" },
185185 "uniqueItems": true
186186}
187187|]
@@ -228,7 +228,7 @@ playerSchemaJSON = [aesonQQ|
228228 {
229229 "position":
230230 {
231− "$ref": "#/components/schemas/Point"
231+ "$ref": "#/definitions/Point"
232232 }
233233 },
234234 "required": ["position"]
@@ -250,7 +250,7 @@ playersSchemaJSON = [aesonQQ|
250250 {
251251 "position":
252252 {
253− "$ref": "#/components/schemas/Point"
253+ "$ref": "#/definitions/Point"
254254 }
255255 },
256256 "required": ["position"]
@@ -271,192 +271,106 @@ instance ToSchema Character
271271characterSchemaJSON :: Value
272272characterSchemaJSON = [aesonQQ|
273273{
274− "oneOf": [
274+ "type": "object",
275+ "properties":
275276 {
276− "required": [
277− "tag",
278− "contents"
279− ],
280− "type": "object",
281− "properties": {
282− "tag": {
283− "type": "string",
284− "enum": [
285− "PC"
286− ]
287− },
288− "contents": {
289− "$ref": "#/components/schemas/Player"
277+ "PC": { "$ref": "#/definitions/Player" },
278+ "NPC":
279+ {
280+ "type": "object",
281+ "properties":
282+ {
283+ "npcName": { "type": "string" },
284+ "npcPosition": { "$ref": "#/definitions/Point" }
285+ },
286+ "required": ["npcName", "npcPosition"]
290287 }
291− }
292288 },
293− {
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"
289+ "maxProperties": 1,
290+ "minProperties": 1
317291}
318−
319292|]
320293
321294characterInlinedSchemaJSON :: Value
322295characterInlinedSchemaJSON = [aesonQQ|
323296{
324− "oneOf": [
297+ "type": "object",
298+ "properties":
325299 {
326− "required": [
327− "tag",
328− "contents"
329− ],
330− "type": "object",
331− "properties": {
332− "tag": {
333− "type": "string",
334− "enum": [
335− "PC"
336− ]
337− },
338− "contents": {
339− "required": [
340− "position"
341− ],
300+ "PC":
301+ {
342302 "type": "object",
343− "properties": {
344− "position": {
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"
303+ "properties":
304+ {
305+ "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"]
358314 }
359− }
360− }
361− }
362− }
363− }
364− },
365− {
366− "required": [
367− "npcName",
368− "npcPosition",
369− "tag"
370− ],
371− "type": "object",
372− "properties": {
373− "tag": {
374− "type": "string",
375− "enum": [
376− "NPC"
377− ]
315+ },
316+ "required": ["position"]
378317 },
379− "npcPosition": {
380− "required": [
381− "x",
382− "y"
383− ],
318+ "NPC":
319+ {
384320 "type": "object",
385− "properties": {
386− "x": {
387− "format": "double",
388− "type": "number"
321+ "properties":
322+ {
323+ "npcName": { "type": "string" },
324+ "npcPosition":
325+ {
326+ "type": "object",
327+ "properties":
328+ {
329+ "x": { "type": "number", "format": "double" },
330+ "y": { "type": "number", "format": "double" }
331+ },
332+ "required": ["x", "y"]
333+ }
389334 },
390− "y": {
391− "format": "double",
392− "type": "number"
393− }
394− }
395− },
396− "npcName": {
397− "type": "string"
335+ "required": ["npcName", "npcPosition"]
398336 }
399− }
400− }
401− ],
402− "type": "object"
337+ },
338+ "maxProperties": 1,
339+ "minProperties": 1
403340}
404341|]
405342
406343characterInlinedPlayerSchemaJSON :: Value
407344characterInlinedPlayerSchemaJSON = [aesonQQ|
408345{
409− "oneOf": [
346+ "type": "object",
347+ "properties":
410348 {
411− "required": [
412− "tag",
413− "contents"
414− ],
415− "type": "object",
416− "properties": {
417− "tag": {
418− "type": "string",
419− "enum": [
420− "PC"
421− ]
349+ "PC":
350+ {
351+ "type": "object",
352+ "properties":
353+ {
354+ "position":
355+ {
356+ "$ref": "#/definitions/Point"
357+ }
358+ },
359+ "required": ["position"]
422360 },
423− "contents": {
424− "required": [
425− "position"
426− ],
361+ "NPC":
362+ {
427363 "type": "object",
428− "properties": {
429− "position": {
430− "$ref": "#/components/schemas/Point"
431− }
432− }
364+ "properties":
365+ {
366+ "npcName": { "type": "string" },
367+ "npcPosition": { "$ref": "#/definitions/Point" }
368+ },
369+ "required": ["npcName", "npcPosition"]
433370 }
434− }
435371 },
436− {
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"
372+ "maxProperties": 1,
373+ "minProperties": 1
460374}
461375|]
462376
@@ -568,7 +482,7 @@ myRoseTreeSchemaJSON = [aesonQQ|
568482 "type": "array",
569483 "items":
570484 {
571− "$ref": "#/components/schemas/RoseTree"
485+ "$ref": "#/definitions/RoseTree"
572486 }
573487 }
574488 },
@@ -597,7 +511,7 @@ myRoseTreeSchemaJSON' = [aesonQQ|
597511 "type": "array",
598512 "items":
599513 {
600− "$ref": "#/components/schemas/myrosetree'"
514+ "$ref": "#/definitions/myrosetree'"
601515 }
602516 }
603517 },
@@ -628,169 +542,42 @@ data Light
628542 deriving (Generic)
629543
630544instance ToSchema Light where
631− declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
545+ declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
632546 { unwrapUnaryRecords = True }
633547
634548lightSchemaJSON :: Value
635549lightSchemaJSON = [aesonQQ|
636550{
637− "oneOf": [
638− {
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",
667− "type": "number"
668− }
669− }
670− },
551+ "type": "object",
552+ "properties":
671553 {
672− "required": [
673− "tag",
674− "contents"
675− ],
676− "type": "object",
677− "properties": {
678− "tag": {
679− "type": "string",
680− "enum": [
681− "LightColor"
682− ]
683− },
684− "contents": {
685− "$ref": "#/components/schemas/Color"
686− }
687− }
554+ "NoLight": { "type": "array", "items": {}, "maxItems": 0, "example": [] },
555+ "LightFreq": { "type": "number", "format": "double" },
556+ "LightColor": { "$ref": "#/definitions/Color" },
557+ "LightWaveLength": { "type": "number", "format": "double" }
688558 },
689− {
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"
559+ "maxProperties": 1,
560+ "minProperties": 1
710561}
711562|]
712563
713564lightInlinedSchemaJSON :: Value
714565lightInlinedSchemaJSON = [aesonQQ|
715566{
716− "oneOf": [
717− {
718− "required": [
719− "tag"
720− ],
721− "type": "object",
722− "properties": {
723− "tag": {
724− "type": "string",
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− },
567+ "type": "object",
568+ "properties":
750569 {
751− "required": [
752− "tag",
753− "contents"
754− ],
755− "type": "object",
756− "properties": {
757− "tag": {
570+ "NoLight": { "type": "array", "items": {}, "maxItems": 0, "example": [] },
571+ "LightFreq": { "type": "number", "format": "double" },
572+ "LightColor":
573+ {
758574 "type": "string",
759− "enum": [
760− "LightColor"
761− ]
575+ "enum": ["Red", "Green", "Blue"]
762576 },
763− "contents": {
764− "type": "string",
765− "enum": [
766− "Red",
767− "Green",
768− "Blue"
769− ]
770− }
771− }
577+ "LightWaveLength": { "type": "number", "format": "double" }
772578 },
773− {
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"
579+ "maxProperties": 1,
580+ "minProperties": 1
794581}
795582|]
796583
@@ -863,129 +650,6 @@ singleMaybeFieldSchemaJSON = [aesonQQ|
863650}
864651|]
865652
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−|]
989653
990654-- ========================================================================
991655-- TimeOfDay
test/Data/OpenApi/ParamSchemaSpec.hs → test/Data/Swagger/ParamSchemaSpec.hsrenamed+5−4
@@ -4,7 +4,7 @@
44{-# LANGUAGE TemplateHaskell #-}
55{-# LANGUAGE QuasiQuotes #-}
66{-# LANGUAGE OverloadedStrings #-}
7−module Data.OpenApi.ParamSchemaSpec where
7+module Data.Swagger.ParamSchemaSpec where
88
99import Data.Aeson
1010import Data.Aeson.QQ.Simple
@@ -12,9 +12,10 @@ import Data.Char
1212import Data.Proxy
1313import GHC.Generics
1414
15−import Data.OpenApi
15+import Data.Swagger
16+import Data.Swagger.Internal (SwaggerKind(..))
1617
17−import Data.OpenApi.CommonTestTypes
18+import Data.Swagger.CommonTestTypes
1819import SpecCommon
1920import Test.Hspec
2021import Data.Time.LocalTime
@@ -22,7 +23,7 @@ import Data.Time.LocalTime
2223import qualified Data.HashMap.Strict as HM
2324
2425checkToParamSchema :: ToParamSchema a => Proxy a -> Value -> Spec
25−checkToParamSchema proxy js = (toParamSchema proxy :: Schema) <=> js
26+checkToParamSchema proxy js = (toParamSchema proxy :: ParamSchema ('SwaggerKindNormal Param)) <=> js
2627
2728spec :: Spec
2829spec = do
test/Data/OpenApi/Schema/GeneratorSpec.hs → test/Data/Swagger/Schema/GeneratorSpec.hsrenamed+7−7
@@ -1,13 +1,13 @@
11{-# LANGUAGE OverloadedLists #-}
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE PackageImports #-}
4−module Data.OpenApi.Schema.GeneratorSpec where
4+module Data.Swagger.Schema.GeneratorSpec where
55
66import Prelude ()
77import Prelude.Compat
88
9−import Data.OpenApi
10−import Data.OpenApi.Schema.Generator
9+import Data.Swagger
10+import Data.Swagger.Schema.Generator
1111
1212import Control.Lens.Operators
1313import Data.Aeson
@@ -103,7 +103,7 @@ instance FromJSON WrongType where
103103instance ToSchema WrongType where
104104 declareNamedSchema _ = return . NamedSchema (Just "WrongType") $
105105 mempty
106− & type_ ?~ OpenApiObject
106+ & type_ ?~ SwaggerObject
107107
108108
109109data MissingRequired = MissingRequired
@@ -123,7 +123,7 @@ instance ToSchema MissingRequired where
123123 boolSchema <- declareSchemaRef (Proxy :: Proxy Bool)
124124 return . NamedSchema (Just "MissingRequired") $
125125 mempty
126− & type_ ?~ OpenApiObject
126+ & type_ ?~ SwaggerObject
127127 & properties .~ [("propA", stringSchema)
128128 ,("propB", boolSchema)
129129 ]
@@ -145,7 +145,7 @@ instance ToSchema MissingProperty where
145145 stringSchema <- declareSchemaRef (Proxy :: Proxy String)
146146 return . NamedSchema (Just "MissingProperty") $
147147 mempty
148− & type_ ?~ OpenApiObject
148+ & type_ ?~ SwaggerObject
149149 & properties .~ [("propC", stringSchema)]
150150 & required .~ ["propC"]
151151
@@ -163,6 +163,6 @@ instance ToSchema WrongPropType where
163163 boolSchema <- declareSchemaRef (Proxy :: Proxy Bool)
164164 return . NamedSchema (Just "WrongPropType") $
165165 mempty
166− & type_ ?~ OpenApiObject
166+ & type_ ?~ SwaggerObject
167167 & properties .~ [("propE", boolSchema)]
168168 & required .~ ["propE"]
test/Data/OpenApi/Schema/ValidationSpec.hs → test/Data/Swagger/Schema/ValidationSpec.hsrenamed+9−56
@@ -1,18 +1,13 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DeriveGeneric #-}
32{-# LANGUAGE PackageImports #-}
43{-# LANGUAGE RecordWildCards #-}
54{-# LANGUAGE ScopedTypeVariables #-}
65{-# OPTIONS_GHC -fno-warn-orphans #-}
7−module Data.OpenApi.Schema.ValidationSpec where
6+module Data.Swagger.Schema.ValidationSpec where
87
98import Control.Applicative
109import Control.Lens ((&), (.~), (?~))
1110import 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
1611import Data.Aeson.Types
1712import Data.Hashable (Hashable)
1813import Data.HashMap.Strict (HashMap)
@@ -33,9 +28,8 @@ import Data.Version (Version)
3328import Data.Word
3429import GHC.Generics
3530
36−import Data.OpenApi
37−import Data.OpenApi.Declare
38−import Data.OpenApi.Aeson.Compat (stringToKey)
31+import Data.Swagger
32+import Data.Swagger.Declare
3933
4034import Test.Hspec
4135import Test.Hspec.QuickCheck
@@ -97,8 +91,6 @@ spec = do
9791 prop "Paint" $ shouldValidate (Proxy :: Proxy Paint)
9892 prop "MyRoseTree" $ shouldValidate (Proxy :: Proxy MyRoseTree)
9993 prop "Light" $ shouldValidate (Proxy :: Proxy Light)
100− prop "Light TaggedObject" $ shouldValidate (Proxy :: Proxy LightTaggedObject)
101− prop "Light UntaggedValue" $ shouldValidate (Proxy :: Proxy LightUntaggedValue)
10294 prop "ButtonImages" $ shouldValidate (Proxy :: Proxy ButtonImages)
10395 prop "Version" $ shouldValidate (Proxy :: Proxy Version)
10496 prop "FreeForm" $ shouldValidate (Proxy :: Proxy FreeForm)
@@ -130,9 +122,9 @@ instance Arbitrary Person where
130122
131123invalidPersonToJSON :: Person -> Value
132124invalidPersonToJSON Person{..} = object
133− [ stringToKey "personName" .= toJSON name
134− , stringToKey "personPhone" .= toJSON phone
135− , stringToKey "personEmail" .= toJSON email
125+ [ T.pack "personName" .= toJSON name
126+ , T.pack "personPhone" .= toJSON phone
127+ , T.pack "personEmail" .= toJSON email
136128 ]
137129
138130-- ========================================================================
@@ -196,10 +188,10 @@ instance Arbitrary MyRoseTree where
196188data Light = NoLight | LightFreq Double | LightColor Color deriving (Show, Generic)
197189
198190instance ToSchema Light where
199− declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions { Data.OpenApi.sumEncoding = ObjectWithSingleField }
191+ declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
200192
201193instance ToJSON Light where
202− toJSON = genericToJSON defaultOptions { Data.Aeson.Types.sumEncoding = ObjectWithSingleField }
194+ toJSON = genericToJSON defaultOptions { sumEncoding = ObjectWithSingleField }
203195
204196instance Arbitrary Light where
205197 arbitrary = oneof
@@ -211,34 +203,6 @@ instance Arbitrary Light where
211203invalidLightToJSON :: Light -> Value
212204invalidLightToJSON = genericToJSON defaultOptions
213205
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−
242206-- ========================================================================
243207-- ButtonImages (bounded enum key mapping)
244208-- ========================================================================
@@ -283,7 +247,7 @@ instance ToJSON FreeForm where
283247
284248instance ToSchema FreeForm where
285249 declareNamedSchema _ = pure $ NamedSchema (Just $ T.pack "FreeForm") $ mempty
286− & type_ ?~ OpenApiObject
250+ & type_ ?~ SwaggerObject
287251 & additionalProperties ?~ AdditionalPropertiesAllowed True
288252
289253instance Arbitrary FreeForm where
@@ -301,8 +265,6 @@ instance Eq ZonedTime where
301265-- Arbitrary instance for Data.Aeson.Value
302266-- ========================================================================
303267
304−-- These instances were introduces in aeson upstream in 2.0.3
305−#if !MIN_VERSION_aeson(2,0,3)
306268instance Arbitrary Value where
307269 -- Weights are almost random
308270 -- Uniform oneof tends not to build complex objects cause of recursive call.
@@ -313,12 +275,3 @@ instance Arbitrary Value where
313275 , (3, Number <$> arbitrary)
314276 , (3, Bool <$> arbitrary)
315277 , (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/OpenApi/SchemaSpec.hs → test/Data/Swagger/SchemaSpec.hsrenamed+11−15
@@ -2,7 +2,7 @@
22{-# LANGUAGE ScopedTypeVariables #-}
33{-# LANGUAGE TemplateHaskell #-}
44{-# LANGUAGE QuasiQuotes #-}
5−module Data.OpenApi.SchemaSpec where
5+module Data.Swagger.SchemaSpec where
66
77import Prelude ()
88import Prelude.Compat
@@ -14,54 +14,51 @@ import Data.Proxy
1414import Data.Set (Set)
1515import qualified Data.Text as Text
1616
17−import Data.OpenApi
18−import Data.OpenApi.Declare
17+import Data.Swagger
18+import Data.Swagger.Declare
1919
20−import Data.OpenApi.CommonTestTypes
20+import Data.Swagger.CommonTestTypes
2121import SpecCommon
2222import Test.Hspec
2323
2424import qualified Data.HashMap.Strict as HM
2525import Data.Time.LocalTime
2626
27−checkToSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
27+checkToSchema :: ToSchema a => Proxy a -> Value -> Spec
2828checkToSchema proxy js = toSchema proxy <=> js
2929
30−checkSchemaName :: (HasCallStack, ToSchema a) => Maybe String -> Proxy a -> Spec
30+checkSchemaName :: ToSchema a => Maybe String -> Proxy a -> Spec
3131checkSchemaName sname proxy =
3232 it ("schema name is " ++ show sname) $
3333 schemaName proxy `shouldBe` fmap Text.pack sname
3434
35−checkDefs :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
35+checkDefs :: ToSchema a => Proxy a -> [String] -> Spec
3636checkDefs proxy names =
3737 it ("uses these definitions " ++ show names) $
3838 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
3939 where
4040 defs = execDeclare (declareNamedSchema proxy) mempty
4141
42−checkProperties :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
42+checkProperties :: ToSchema a => Proxy a -> [String] -> Spec
4343checkProperties proxy names =
4444 it ("has these fields in order " ++ show names) $
4545 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
4646 where
4747 fields = toSchema proxy ^. properties
4848
49−checkInlinedSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
49+checkInlinedSchema :: ToSchema a => Proxy a -> Value -> Spec
5050checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
5151
52−checkInlinedSchemas :: (HasCallStack, ToSchema a) => [String] -> Proxy a -> Value -> Spec
52+checkInlinedSchemas :: ToSchema a => [String] -> Proxy a -> Value -> Spec
5353checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
5454 where
5555 (defs, s) = runDeclare (declareSchema proxy) mempty
5656
57−checkInlinedRecSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
57+checkInlinedRecSchema :: ToSchema a => Proxy a -> Value -> Spec
5858checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
5959 where
6060 (defs, s) = runDeclare (declareSchema proxy) mempty
6161
62−checkToSchemaDeclare :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
63−checkToSchemaDeclare proxy js = runDeclare (declareSchemaRef proxy) mempty <=> js
64−
6562spec :: Spec
6663spec = do
6764 describe "Generic ToSchema" $ do
@@ -81,7 +78,6 @@ spec = do
8178 context "UserId (non-record newtype)" $ checkToSchema (Proxy :: Proxy UserId) userIdSchemaJSON
8279 context "Player (unary record)" $ checkToSchema (Proxy :: Proxy Player) playerSchemaJSON
8380 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
8581 context "Players (inlining schema)" $ checkToSchema (Proxy :: Proxy Players) playersSchemaJSON
8682 context "MyRoseTree (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree) myRoseTreeSchemaJSON
8783 context "MyRoseTree' (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree') myRoseTreeSchemaJSON'
test/Data/SwaggerSpec.hsnew file+1715
@@ -0,0 +1,1715 @@
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+9−1
@@ -8,7 +8,15 @@ import qualified Data.Vector as Vector
88
99import Test.Hspec
1010
11−(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a, HasCallStack) => a -> Value -> Spec
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
1220x <=> js = do
1321 it "encodes correctly" $ do
1422 toJSON x `shouldBe` js