git.iko.soy/typeable/openapi3
on master help sign in
41 files changed+5641−4462
since splitsnapshots
unifiedsplit
.ghci−1 .github/workflows/haskell-ci.yml+243 .travis.yml−120 CHANGELOG.md+88 README.md+13−11 cabal.haskell-ci+11 cabal.project+4 examples/hackage.hs+16−20 include/overlapping-compat.h−8 openapi3.cabal+160 src/Data/Swagger.hs → src/Data/OpenApi.hs+197−114 src/Data/OpenApi/Aeson/Compat.hs+76 src/Data/Swagger/Declare.hs → src/Data/OpenApi/Declare.hs+5−9 src/Data/OpenApi/Internal.hs+1619 src/Data/Swagger/Internal/AesonUtils.hs → src/Data/OpenApi/Internal/AesonUtils.hs+17−36 src/Data/Swagger/Internal/ParamSchema.hs → src/Data/OpenApi/Internal/ParamSchema.hs+90−67 src/Data/Swagger/Internal/Schema.hs → src/Data/OpenApi/Internal/Schema.hs+459−211 src/Data/Swagger/Internal/Schema/Validation.hs → src/Data/OpenApi/Internal/Schema/Validation.hs+218−73 src/Data/Swagger/Internal/TypeShape.hs → src/Data/OpenApi/Internal/TypeShape.hs+1−11 src/Data/Swagger/Internal/Utils.hs → src/Data/OpenApi/Internal/Utils.hs+7−1 src/Data/OpenApi/Lens.hs+166 src/Data/OpenApi/Operation.hs+305 src/Data/OpenApi/Optics.hs+328 src/Data/Swagger/ParamSchema.hs → src/Data/OpenApi/ParamSchema.hs+7−7 src/Data/Swagger/Schema.hs → src/Data/OpenApi/Schema.hs+4−13 src/Data/Swagger/Schema/Generator.hs → src/Data/OpenApi/Schema/Generator.hs+37−26 src/Data/Swagger/Schema/Validation.hs → src/Data/OpenApi/Schema/Validation.hs+9−7 src/Data/Swagger/SchemaOptions.hs → src/Data/OpenApi/SchemaOptions.hs+7−3 src/Data/Swagger/Internal.hs−1386 src/Data/Swagger/Lens.hs−180 src/Data/Swagger/Operation.hs−192 stack.yaml+6−4 swagger2.cabal−151 test/Data/Swagger/CommonTestTypes.hs → test/Data/OpenApi/CommonTestTypes.hs+492−102 test/Data/Swagger/ParamSchemaSpec.hs → test/Data/OpenApi/ParamSchemaSpec.hs+10−6 test/Data/Swagger/Schema/GeneratorSpec.hs → test/Data/OpenApi/Schema/GeneratorSpec.hs+7−7 test/Data/Swagger/Schema/ValidationSpec.hs → test/Data/OpenApi/Schema/ValidationSpec.hs+55−9 test/Data/Swagger/SchemaSpec.hs → test/Data/OpenApi/SchemaSpec.hs+21−17 test/Data/OpenApiSpec.hs+962 test/Data/SwaggerSpec.hs−1661 test/SpecCommon.hs+1−9
.ghcideleted−1
@@ -1 +0,0 @@
1−:set -itest -isrc -optP-include -optP.stack-work/dist/x86_64-linux/Cabal-1.24.2.0/build/autogen/cabal_macros.h -optP-I -optPinclude
.github/workflows/haskell-ci.ymlnew file+243
@@ -0,0 +1,243 @@
1+# This GitHub workflow config has been generated by a script via
2+#
3+# haskell-ci 'github' 'cabal.project'
4+#
5+# To regenerate the script (for example after adjusting tested-with) run
6+#
7+# haskell-ci regenerate
8+#
9+# For more information, see https://github.com/haskell-CI/haskell-ci
10+#
11+# version: 0.14.3
12+#
13+# REGENDATA ("0.14.3",["github","cabal.project"])
14+#
15+name: Haskell-CI
16+on:
17+ push:
18+ branches:
19+ - master
20+ pull_request:
21+ branches:
22+ - master
23+jobs:
24+ linux:
25+ name: Haskell-CI - Linux - ${{ matrix.compiler }}
26+ runs-on: ubuntu-18.04
27+ timeout-minutes:
28+ 60
29+ container:
30+ image: buildpack-deps:focal
31+ continue-on-error: ${{ matrix.allow-failure }}
32+ strategy:
33+ matrix:
34+ include:
35+ - compiler: ghc-9.2.2
36+ compilerKind: ghc
37+ compilerVersion: 9.2.2
38+ setup-method: ghcup
39+ allow-failure: true
40+ - compiler: ghc-9.0.2
41+ compilerKind: ghc
42+ compilerVersion: 9.0.2
43+ setup-method: ghcup
44+ allow-failure: false
45+ - compiler: ghc-8.10.7
46+ compilerKind: ghc
47+ compilerVersion: 8.10.7
48+ setup-method: ghcup
49+ allow-failure: false
50+ - compiler: ghc-8.8.4
51+ compilerKind: ghc
52+ compilerVersion: 8.8.4
53+ setup-method: hvr-ppa
54+ allow-failure: false
55+ - compiler: ghc-8.6.5
56+ compilerKind: ghc
57+ compilerVersion: 8.6.5
58+ setup-method: hvr-ppa
59+ allow-failure: false
60+ - compiler: ghc-8.4.4
61+ compilerKind: ghc
62+ compilerVersion: 8.4.4
63+ setup-method: hvr-ppa
64+ allow-failure: false
65+ fail-fast: false
66+ steps:
67+ - name: apt
68+ run: |
69+ apt-get update
70+ apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
71+ if [ "${{ matrix.setup-method }}" = ghcup ]; then
72+ mkdir -p "$HOME/.ghcup/bin"
73+ curl -sL https://downloads.haskell.org/ghcup/0.1.17.5/x86_64-linux-ghcup-0.1.17.5 > "$HOME/.ghcup/bin/ghcup"
74+ chmod a+x "$HOME/.ghcup/bin/ghcup"
75+ "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER"
76+ "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0
77+ else
78+ apt-add-repository -y 'ppa:hvr/ghc'
79+ apt-get update
80+ apt-get install -y "$HCNAME"
81+ mkdir -p "$HOME/.ghcup/bin"
82+ curl -sL https://downloads.haskell.org/ghcup/0.1.17.5/x86_64-linux-ghcup-0.1.17.5 > "$HOME/.ghcup/bin/ghcup"
83+ chmod a+x "$HOME/.ghcup/bin/ghcup"
84+ "$HOME/.ghcup/bin/ghcup" install cabal 3.6.2.0
85+ fi
86+ env:
87+ HCKIND: ${{ matrix.compilerKind }}
88+ HCNAME: ${{ matrix.compiler }}
89+ HCVER: ${{ matrix.compilerVersion }}
90+ - name: Set PATH and environment variables
91+ run: |
92+ echo "$HOME/.cabal/bin" >> $GITHUB_PATH
93+ echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
94+ echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
95+ echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
96+ HCDIR=/opt/$HCKIND/$HCVER
97+ if [ "${{ matrix.setup-method }}" = ghcup ]; then
98+ HC=$HOME/.ghcup/bin/$HCKIND-$HCVER
99+ echo "HC=$HC" >> "$GITHUB_ENV"
100+ echo "HCPKG=$HOME/.ghcup/bin/$HCKIND-pkg-$HCVER" >> "$GITHUB_ENV"
101+ echo "HADDOCK=$HOME/.ghcup/bin/haddock-$HCVER" >> "$GITHUB_ENV"
102+ echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
103+ else
104+ HC=$HCDIR/bin/$HCKIND
105+ echo "HC=$HC" >> "$GITHUB_ENV"
106+ echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
107+ echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
108+ echo "CABAL=$HOME/.ghcup/bin/cabal-3.6.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
109+ fi
110+
111+ HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
112+ echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
113+ echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
114+ echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
115+ echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
116+ echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
117+ echo "GHCJSARITH=0" >> "$GITHUB_ENV"
118+ env:
119+ HCKIND: ${{ matrix.compilerKind }}
120+ HCNAME: ${{ matrix.compiler }}
121+ HCVER: ${{ matrix.compilerVersion }}
122+ - name: env
123+ run: |
124+ env
125+ - name: write cabal config
126+ run: |
127+ mkdir -p $CABAL_DIR
128+ cat >> $CABAL_CONFIG <<EOF
129+ remote-build-reporting: anonymous
130+ write-ghc-environment-files: never
131+ remote-repo-cache: $CABAL_DIR/packages
132+ logs-dir: $CABAL_DIR/logs
133+ world-file: $CABAL_DIR/world
134+ extra-prog-path: $CABAL_DIR/bin
135+ symlink-bindir: $CABAL_DIR/bin
136+ installdir: $CABAL_DIR/bin
137+ build-summary: $CABAL_DIR/logs/build.log
138+ store-dir: $CABAL_DIR/store
139+ install-dirs user
140+ prefix: $CABAL_DIR
141+ repository hackage.haskell.org
142+ url: http://hackage.haskell.org/
143+ EOF
144+ cat >> $CABAL_CONFIG <<EOF
145+ program-default-options
146+ ghc-options: $GHCJOBS +RTS -M3G -RTS
147+ EOF
148+ cat $CABAL_CONFIG
149+ - name: versions
150+ run: |
151+ $HC --version || true
152+ $HC --print-project-git-commit-id || true
153+ $CABAL --version || true
154+ - name: update cabal index
155+ run: |
156+ $CABAL v2-update -v
157+ - name: install cabal-plan
158+ run: |
159+ mkdir -p $HOME/.cabal/bin
160+ curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.6.2.0/cabal-plan-0.6.2.0-x86_64-linux.xz > cabal-plan.xz
161+ echo 'de73600b1836d3f55e32d80385acc055fd97f60eaa0ab68a755302685f5d81bc cabal-plan.xz' | sha256sum -c -
162+ xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
163+ rm -f cabal-plan.xz
164+ chmod a+x $HOME/.cabal/bin/cabal-plan
165+ cabal-plan --version
166+ - name: checkout
167+ uses: actions/checkout@v2
168+ with:
169+ path: source
170+ - name: initial cabal.project for sdist
171+ run: |
172+ touch cabal.project
173+ echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project
174+ cat cabal.project
175+ - name: sdist
176+ run: |
177+ mkdir -p sdist
178+ $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
179+ - name: unpack
180+ run: |
181+ mkdir -p unpacked
182+ find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
183+ - name: generate cabal.project
184+ run: |
185+ PKGDIR_openapi3="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/openapi3-[0-9.]*')"
186+ echo "PKGDIR_openapi3=${PKGDIR_openapi3}" >> "$GITHUB_ENV"
187+ rm -f cabal.project cabal.project.local
188+ touch cabal.project
189+ touch cabal.project.local
190+ echo "packages: ${PKGDIR_openapi3}" >> cabal.project
191+ echo "package openapi3" >> cabal.project
192+ echo " ghc-options: -Werror=missing-methods" >> cabal.project
193+ cat >> cabal.project <<EOF
194+ allow-newer: http-media:base
195+ EOF
196+ $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(openapi3)$/; }' >> cabal.project.local
197+ cat cabal.project
198+ cat cabal.project.local
199+ - name: dump install plan
200+ run: |
201+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
202+ cabal-plan
203+ - name: cache
204+ uses: actions/cache@v2
205+ with:
206+ key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
207+ path: ~/.cabal/store
208+ restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
209+ - name: install dependencies
210+ run: |
211+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
212+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
213+ - name: build w/o tests
214+ run: |
215+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
216+ - name: build
217+ run: |
218+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
219+ - name: tests
220+ run: |
221+ $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
222+ - name: cabal check
223+ run: |
224+ cd ${PKGDIR_openapi3} || false
225+ ${CABAL} -vnormal check
226+ - name: haddock
227+ run: |
228+ $CABAL v2-haddock $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
229+ - name: unconstrained build
230+ run: |
231+ rm -f cabal.project.local
232+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
233+ - name: prepare for constraint sets
234+ run: |
235+ rm -f cabal.project.local
236+ - name: constraint set aeson-2
237+ run: |
238+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson >=2.0' --dependencies-only -j2 all
239+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson >=2.0' all
240+ - name: constraint set aeson-1
241+ run: |
242+ if [ $((HCNUMVER < 90200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson <2.0' --dependencies-only -j2 all ; fi
243+ if [ $((HCNUMVER < 90200)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='aeson <2.0' all ; fi
.travis.ymldeleted−120
@@ -1,120 +0,0 @@
1−# This Travis job script has been generated by a script via
2−#
3−# haskell-ci '--branches' 'master' '--haddock=>=8.4' '--output' '.travis.yml' 'swagger2.cabal'
4−#
5−# For more information, see https://github.com/haskell-CI/haskell-ci
6−#
7−language: c
8−dist: xenial
9−
10−git:
11− submodules: false # whether to recursively clone submodules
12−
13−branches:
14− only:
15− - master
16−
17−cache:
18− directories:
19− - $HOME/.cabal/packages
20− - $HOME/.cabal/store
21−
22−before_cache:
23− - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
24− # remove files that are regenerated by 'cabal update'
25− - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
26− - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
27− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
28− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
29− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
30−
31− - rm -rfv $CABALHOME/packages/head.hackage
32−
33−matrix:
34− include:
35− - compiler: "ghc-8.6.2"
36− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.2], sources: [hvr-ghc]}}
37− - compiler: "ghc-8.4.4"
38− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.4.4], sources: [hvr-ghc]}}
39− - compiler: "ghc-8.2.2"
40− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
41− - compiler: "ghc-8.0.2"
42− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}
43− - compiler: "ghc-7.10.3"
44− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}
45− - compiler: "ghc-7.8.4"
46− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}
47−
48−before_install:
49− - HC=/opt/ghc/bin/${CC}
50− - HCPKG=${HC/ghc/ghc-pkg}
51− - unset CC
52− - CABAL=/opt/ghc/bin/cabal
53− - CABALHOME=$HOME/.cabal
54− - "export PATH=\"$CABALHOME/bin:$PATH\""
55− - ROOTDIR=$(pwd)
56− - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
57− - echo $HCNUMVER
58−
59−install:
60− - ${CABAL} --version
61− - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
62− - TEST=--enable-tests
63− - BENCH=--enable-benchmarks
64− - GHCHEAD=${GHCHEAD-false}
65− - travis_retry ${CABAL} update -v
66− - "sed -i.bak 's/^jobs:/-- jobs:/' $CABALHOME/config"
67− - rm -fv cabal.project cabal.project.local
68− - grep -Ev -- '^\s*--' $CABALHOME/config | grep -Ev '^\s*$'
69− - rm -f cabal.project
70− - touch cabal.project
71− - "printf 'packages: \".\"\\n' >> cabal.project"
72− - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"
73− - touch cabal.project.local
74− - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(swagger2)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
75− - cat cabal.project || true
76− - cat cabal.project.local || true
77− - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
78− - rm -f cabal.project.freeze
79− - ${CABAL} new-freeze -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dry
80− - "cat \"cabal.project.freeze\" | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
81− - rm "cabal.project.freeze"
82− - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
83− - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
84− - rm -rf .ghc.environment.* "."/dist
85− - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
86−
87−# Here starts the actual work to be performed for the package under test;
88−# any command which exits with a non-zero exit code causes the build to fail.
89−script:
90− # test that source-distributions can be generated
91− - ${CABAL} new-sdist all
92− - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
93− - cd ${DISTDIR} || false
94− - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
95− - rm -f cabal.project
96− - touch cabal.project
97− - "printf 'packages: \"swagger2-*/*.cabal\"\\n' >> cabal.project"
98− - "printf 'write-ghc-environment-files: always\\n' >> cabal.project"
99− - touch cabal.project.local
100− - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | grep -vE -- '^(swagger2)$' | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
101− - cat cabal.project || true
102− - cat cabal.project.local || true
103− # this builds all libraries and executables (without tests/benchmarks)
104− - ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all
105−
106− # build & run tests, build benchmarks
107− - ${CABAL} new-build -w ${HC} ${TEST} ${BENCH} all
108− - if [ "x$TEST" = "x--enable-tests" ]; then ${CABAL} new-test -w ${HC} ${TEST} ${BENCH} all; fi
109−
110− # ${CABAL} check
111− - (cd swagger2-* && ${CABAL} check)
112−
113− # haddock
114− - if [ $HCNUMVER -ge 80400 ] ; then ${CABAL} new-haddock -w ${HC} ${TEST} ${BENCH} all ; fi
115−
116− # Build without installed constraints for packages in global-db
117− - rm -f cabal.project.local; ${CABAL} new-build -w ${HC} --disable-tests --disable-benchmarks all;
118−
119−# REGENDATA ["--branches","master","--haddock=>=8.4","--output",".travis.yml","swagger2.cabal"]
120−# EOF
CHANGELOG.md+88
@@ -1,5 +1,93 @@
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+
48+2.6.1
49+-----
50+
51+- More GHC-8.10 related cleanup, tighten lower bound on some dependencies.
52+ (see [#216]( https://github.com/GetShopTV/swagger2/pull/216 ));
53+- Add "format" field for uint32 and uint64
54+ (see [#215]( https://github.com/GetShopTV/swagger2/pull/215 ));
55+
56+2.6
57+---
58+
59+- GHC 8.10 support (see [#210]( https://github.com/GetShopTV/swagger2/pull/210 ));
60+- Move `aeson-pretty` to other dependencies (see [#206]( https://github.com/GetShopTV/swagger2/pull/206 ));
61+- Merge OAuth2 scopes (see [#151]( https://github.com/GetShopTV/swagger2/pull/151 ));
62+
63+2.5
64+---
65+
66+Highlights:
67+- Add instance for `TimeOfDay` (see [#196]( https://github.com/GetShopTV/swagger2/pull/196 ));
68+- Add support for [`optics`](https://hackage.haskell.org/package/optics) (see [#200]( https://github.com/GetShopTV/swagger2/pull/200 ));
69+- Slightly better validation errors (see [#195]( https://github.com/GetShopTV/swagger2/pull/195 ));
70+
71+Fixes and minor changes:
72+- Minor cleanup (see [#194]( https://github.com/GetShopTV/swagger2/pull/194 ));
73+- Allow base-4.13 and other GHC-8.8 related stuff (see [#198]( https://github.com/GetShopTV/swagger2/pull/198 ));
74+- Fix `stack.yaml` (see [#201]( https://github.com/GetShopTV/swagger2/pull/201 ));
75+
76+2.4
77+---
78+
79+- Allow hashable-1.3, semigroups-0.19, network-3.1, time-1.9, generics-sop-0.5
80+- Tags aren't sorted
81+ (see [#165](https://github.com/GetShopTV/swagger2/issues/165))
82+- Schema type is optional
83+ (see [#138](https://github.com/GetShopTV/swagger2/issues/138), [#164](https://github.com/GetShopTV/swagger2/pull/164))
84+- Take concrete 'Proxy' as argument
85+ (see [#180](https://github.com/GetShopTV/swagger2/pull/180))
86+- GHC-8.8 preparations
87+
1882.3.1.1
289-------
90+
391- Allow `network-3.0`
492
5932.3.1
README.md+13−11
@@ -1,21 +1,22 @@
1−# swagger2
1+# OpenApi 3
22
3−[![Join the chat at https://gitter.im/haskell-swagger2/Lobby](https://badges.gitter.im/haskell-swagger2/Lobby.svg)](https://gitter.im/haskell-swagger2/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3+[![Hackage](https://img.shields.io/hackage/v/openapi3.svg)](http://hackage.haskell.org/package/openapi3)
4+[![Build Status](https://travis-ci.org/biocad/openapi3.svg?branch=master)](https://travis-ci.org/biocad/openapi3)
5+[![Stackage LTS](http://stackage.org/package/openapi3/badge/lts)](http://stackage.org/lts/package/opeopenapi3)
6+[![Stackage Nightly](http://stackage.org/package/openapi3/badge/nightly)](http://stackage.org/nightly/package/openapi3)
47
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)
8+OpenAPI 3.0 data model.
99
10−Swagger 2.0 data model.
10+The original OpenAPI 3.0 specification is available at http://swagger.io/specification/.
1111
12−The original Swagger 2.0 specification is available at http://swagger.io/specification/.
12+This package is heavily based on excellent work on Swagger 2.0 at
13+https://github.com/GetShopTV/swagger2.
1314
1415## Usage
1516
16−This library is intended to be used for decoding and encoding Swagger 2.0 API specifications as well as manipulating them.
17+This library is intended to be used for decoding and encoding OpenApi 3.0.3 specifications as well as manipulating them.
1718
18−Please refer to [haddock documentation](http://hackage.haskell.org/package/swagger2).
19+Please refer to [haddock documentation](http://hackage.haskell.org/package/openapi3).
1920
2021Some examples can be found in [`examples/` directory](/examples).
2122
@@ -33,7 +34,8 @@ Many Swagger tools, including server and client code generation for many languag
3334
3435We are happy to receive bug reports, fixes, documentation enhancements, and other improvements.
3536
36−Please report bugs via the [github issue tracker](https://github.com/GetShopTV/swagger2/issues).
37+Please report bugs via the [github issue tracker](https://github.com/biocad/openapi3/issues).
3738
3839*GetShopTV Team*
3940
41+*Biocad Team*
cabal.haskell-cinew file+11
@@ -0,0 +1,11 @@
1+branches: master
2+
3+distribution: focal
4+allow-failures: >=9.2
5+
6+constraint-set aeson-1
7+ constraints: aeson <2.0
8+ ghc: <9.2
9+
10+constraint-set aeson-2
11+ constraints: aeson >=2.0
cabal.projectnew file+4
@@ -0,0 +1,4 @@
1+packages: .
2+tests: true
3+
4+allow-newer: http-media:base
examples/hackage.hs+16−20
@@ -10,10 +10,10 @@ import Data.Proxy
1010import Data.Text (Text)
1111import GHC.Generics
1212
13−import Data.Swagger
14−import Data.Swagger.Declare
15−import Data.Swagger.Lens
16−import Data.Swagger.Operation
13+import Data.OpenApi
14+import Data.OpenApi.Declare
15+import Data.OpenApi.Lens
16+import Data.OpenApi.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_ .~ SwaggerObject
30+ & type_ ?~ OpenApiObject
3131 & properties .~
3232 [ ("summaryUsername", usernameSchema )
3333 , ("summaryUserid" , useridSchema )
@@ -47,40 +47,36 @@ data UserDetailed = UserDetailed
4747newtype Package = Package { packageName :: Text }
4848 deriving (Generic, ToSchema)
4949
50−hackageSwagger :: Swagger
51−hackageSwagger = spec & definitions .~ defs
50+hackageOpenApi :: OpenApi
51+hackageOpenApi = spec & components.schemas .~ defs
5252 where
53− (defs, spec) = runDeclare declareHackageSwagger mempty
53+ (defs, spec) = runDeclare declareHackageOpenApi mempty
5454
55−declareHackageSwagger :: Declare (Definitions Schema) Swagger
56−declareHackageSwagger = do
55+declareHackageOpenApi :: Declare (Definitions Schema) OpenApi
56+declareHackageOpenApi = do
5757 -- param schemas
5858 let usernameParamSchema = toParamSchema (Proxy :: Proxy Username)
5959
6060 -- responses
61− userSummaryResponse <- declareResponse (Proxy :: Proxy UserSummary)
62− userDetailedResponse <- declareResponse (Proxy :: Proxy UserDetailed)
63− packagesResponse <- declareResponse (Proxy :: Proxy [Package])
61+ userSummaryResponse <- declareResponse "application/json" (Proxy :: Proxy UserSummary)
62+ userDetailedResponse <- declareResponse "application/json" (Proxy :: Proxy UserDetailed)
63+ packagesResponse <- declareResponse "application/json" (Proxy :: Proxy [Package])
6464
6565 return $ mempty
6666 & paths .~
6767 [ ("/users", mempty & get ?~ (mempty
68− & produces ?~ MimeList ["application/json"]
6968 & at 200 ?~ Inline userSummaryResponse))
7069 , ("/user/{username}", mempty & get ?~ (mempty
71− & produces ?~ MimeList ["application/json"]
7270 & parameters .~ [ Inline $ mempty
7371 & name .~ "username"
7472 & required ?~ True
75− & schema .~ ParamOther (mempty
76− & in_ .~ ParamPath
77− & paramSchema .~ usernameParamSchema) ]
73+ & in_ .~ ParamPath
74+ & schema ?~ Inline usernameParamSchema ]
7875 & at 200 ?~ Inline userDetailedResponse))
7976 , ("/packages", mempty & get ?~ (mempty
80− & produces ?~ MimeList ["application/json"]
8177 & at 200 ?~ Inline packagesResponse))
8278 ]
8379
8480main :: IO ()
85−main = putStrLn . read . show . encode $ hackageSwagger
81+main = putStrLn . read . show . encode $ hackageOpenApi
8682
include/overlapping-compat.hdeleted−8
@@ -1,8 +0,0 @@
1−#if __GLASGOW_HASKELL__ >= 710
2−#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}
3−#define OVERLAPPING_ {-# OVERLAPPING #-}
4−#else
5−{-# LANGUAGE OverlappingInstances #-}
6−#define OVERLAPPABLE_
7−#define OVERLAPPING_
8−#endif
openapi3.cabalnew file+160
@@ -0,0 +1,160 @@
1+cabal-version: >=1.10
2+name: openapi3
3+version: 3.2.2
4+
5+synopsis: OpenAPI 3.0 data model
6+category: Web, Swagger, OpenApi
7+description:
8+ This library is intended to be used for decoding and encoding OpenAPI 3.0 API
9+ specifications as well as manipulating them.
10+ .
11+ The original OpenAPI 3.0 specification is available at http://swagger.io/specification/.
12+
13+homepage: https://github.com/biocad/openapi3
14+bug-reports: https://github.com/biocad/openapi3/issues
15+license: BSD3
16+license-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
20+build-type: Custom
21+extra-source-files:
22+ README.md
23+ , CHANGELOG.md
24+ , examples/*.hs
25+tested-with:
26+ GHC ==8.4.4
27+ || ==8.6.5
28+ || ==8.8.4
29+ || ==8.10.7
30+ || ==9.0.2
31+ || ==9.2.2
32+
33+custom-setup
34+ setup-depends:
35+ base, Cabal, cabal-doctest >=1.0.6 && <1.1
36+
37+library
38+ hs-source-dirs: src
39+ 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
50+
51+ -- 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
61+
62+ -- GHC boot libraries
63+ 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
70+
71+ build-depends:
72+ mtl >=2.2.2 && <2.3
73+ , text >=1.2.3.1 && <2.1
74+
75+ -- other dependencies
76+ 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
79+ , aeson-pretty >=0.8.7 && <0.9
80+ -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint
81+ , cookie >=0.4.3 && <0.5
82+ , generics-sop >=0.5.1.0 && <0.6
83+ , hashable >=1.2.7.0 && <1.5
84+ , http-media >=0.8.0.0 && <0.9
85+ , insert-ordered-containers >=0.2.3 && <0.3
86+ , lens >=4.16.1 && <5.2
87+ , network >=2.6.3.5 && <3.2
88+ , optics-core >=0.2 && <0.5
89+ , optics-th >=0.2 && <0.5
90+ , scientific >=0.3.6.2 && <0.4
91+ , unordered-containers >=0.2.9.0 && <0.3
92+ , uuid-types >=1.0.3 && <1.1
93+ , vector >=0.12.0.1 && <0.13
94+ , QuickCheck >=2.10.1 && <2.15
95+
96+ default-language: Haskell2010
97+
98+test-suite spec
99+ type: exitcode-stdio-1.0
100+ hs-source-dirs: test
101+ main-is: Spec.hs
102+
103+ -- From library parat
104+ -- We need aeson's toEncoding for doctests too
105+ build-depends:
106+ base
107+ , QuickCheck
108+ , aeson
109+ , base-compat-batteries
110+ , bytestring
111+ , containers
112+ , hashable
113+ , insert-ordered-containers
114+ , lens
115+ , mtl
116+ , openapi3
117+ , template-haskell
118+ , text
119+ , time
120+ , unordered-containers
121+ , vector
122+
123+ -- test-suite only dependencies
124+ build-depends:
125+ hspec >=2.5.5 && <2.10
126+ , HUnit >=1.6.0.0 && <1.7
127+ , quickcheck-instances >=0.3.19 && <0.14
128+ , utf8-string >=1.0.1.1 && <1.1
129+
130+ -- https://github.com/haskell/cabal/issues/3708
131+ build-tool-depends:
132+ hspec-discover:hspec-discover >=2.5.5 && <2.10
133+
134+ other-modules:
135+ 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
142+ default-language: Haskell2010
143+
144+test-suite doctests
145+ -- See QuickCheck note in https://github.com/phadej/cabal-doctest#notes
146+ build-depends: base, doctest, Glob, QuickCheck
147+ default-language: Haskell2010
148+ hs-source-dirs: test
149+ main-is: doctests.hs
150+ type: exitcode-stdio-1.0
151+
152+executable example
153+ hs-source-dirs: examples
154+ main-is: hackage.hs
155+ default-language: Haskell2010
156+ build-depends: base, aeson, lens, openapi3, text
157+
158+source-repository head
159+ type: git
160+ location: https://github.com/biocad/openapi3.git
src/Data/Swagger.hs → src/Data/OpenApi.hsrenamed+197−114
@@ -1,6 +1,5 @@
1−{-# LANGUAGE CPP #-}
21-- |
3−-- Module: Data.Swagger
2+-- Module: Data.OpenApi
43-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
54-- Stability: experimental
65--
@@ -10,7 +9,7 @@
109-- These files can then be used by the Swagger-UI project to display the API
1110-- and Swagger-Codegen to generate clients in various languages.
1211-- Additional utilities can also take advantage of the resulting files, such as testing tools.
13−module Data.Swagger (
12+module Data.OpenApi (
1413 -- * How to use this library
1514 -- $howto
1615
@@ -30,16 +29,18 @@ module Data.Swagger (
3029 -- $validation
3130
3231 -- * Re-exports
33− module Data.Swagger.Lens,
34− module Data.Swagger.Operation,
35− module Data.Swagger.ParamSchema,
36− module Data.Swagger.Schema,
37− module Data.Swagger.Schema.Validation,
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,
3838
3939 -- * Swagger specification
40− Swagger(..),
41− Host(..),
42− Scheme(..),
40+ OpenApi(..),
41+ Server(..),
42+ ServerVariable(..),
43+ Components(..),
4344
4445 -- ** Info types
4546 Info(..),
@@ -55,38 +56,43 @@ module Data.Swagger (
5556 TagName,
5657
5758 -- ** Types and formats
58− SwaggerType(..),
59+ OpenApiType(..),
5960 Format,
6061 Definitions,
61− CollectionFormat(..),
62+ Style(..),
6263
6364 -- ** Parameters
6465 Param(..),
65− ParamAnySchema(..),
66− ParamOtherSchema(..),
6766 ParamLocation(..),
6867 ParamName,
6968 Header(..),
7069 HeaderName,
7170 Example(..),
71+ RequestBody(..),
72+ MediaTypeObject(..),
73+ Encoding(..),
7274
7375 -- ** Schemas
74− ParamSchema(..),
7576 Schema(..),
7677 NamedSchema(..),
77− SwaggerItems(..),
78+ OpenApiItems(..),
7879 Xml(..),
7980 Pattern,
8081 AdditionalProperties(..),
82+ Discriminator(..),
8183
8284 -- ** Responses
8385 Responses(..),
8486 Response(..),
8587 HttpStatusCode,
88+ Link(..),
89+ Callback(..),
8690
8791 -- ** Security
8892 SecurityScheme(..),
8993 SecuritySchemeType(..),
94+ HttpSchemeType(..),
95+ SecurityDefinitions(..),
9096 SecurityRequirement(..),
9197
9298 -- *** API key
@@ -94,8 +100,12 @@ module Data.Swagger (
94100 ApiKeyLocation(..),
95101
96102 -- *** OAuth2
97− OAuth2Params(..),
103+ OAuth2Flows(..),
98104 OAuth2Flow(..),
105+ OAuth2ImplicitFlow(..),
106+ OAuth2PasswordFlow(..),
107+ OAuth2ClientCredentialsFlow(..),
108+ OAuth2AuthorizationCodeFlow(..),
99109 AuthorizationURL,
100110 TokenURL,
101111
@@ -111,13 +121,14 @@ module Data.Swagger (
111121 URL(..),
112122) where
113123
114−import Data.Swagger.Lens
115−import Data.Swagger.Operation
116−import Data.Swagger.ParamSchema
117−import Data.Swagger.Schema
118−import Data.Swagger.Schema.Validation
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
119130
120−import Data.Swagger.Internal
131+import Data.OpenApi.Internal
121132
122133-- $setup
123134-- >>> import Control.Lens
@@ -125,6 +136,8 @@ import Data.Swagger.Internal
125136-- >>> import Data.Monoid
126137-- >>> import Data.Proxy
127138-- >>> import GHC.Generics
139+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
140+-- >>> import Data.OpenApi.Internal.Utils
128141-- >>> :set -XDeriveGeneric
129142-- >>> :set -XOverloadedStrings
130143-- >>> :set -XOverloadedLists
@@ -141,8 +154,16 @@ import Data.Swagger.Internal
141154--
142155-- In this library you can use @'mempty'@ for a default/empty value. For instance:
143156--
144−-- >>> encode (mempty :: Swagger)
145−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"}}"
157+-- >>> BSL.putStrLn $ encodePretty (mempty :: OpenApi)
158+-- {
159+-- "components": {},
160+-- "info": {
161+-- "title": "",
162+-- "version": ""
163+-- },
164+-- "openapi": "3.0.0",
165+-- "paths": {}
166+-- }
146167--
147168-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
148169-- That is because these properties are actually required ones.
@@ -150,45 +171,87 @@ import Data.Swagger.Internal
150171-- You /should/ always override the default (empty) value for these properties,
151172-- although it is not strictly necessary:
152173--
153−-- >>> encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
154−-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
174+-- >>> BSL.putStrLn $ encodePretty mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
175+-- {
176+-- "title": "Todo API",
177+-- "version": "1.0"
178+-- }
155179--
156180-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
157181--
158−-- >>> encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
159−-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
182+-- >>> BSL.putStrLn $ encodePretty $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
183+-- {
184+-- "title": "Todo API",
185+-- "version": "1.0"
186+-- }
187+
160188--
161189-- This can be useful for combining specifications of endpoints into a whole API specification:
162190--
163191-- @
164192-- \-\- /account subAPI specification
165−-- accountAPI :: Swagger
193+-- accountAPI :: OpenApi
166194--
167195-- \-\- /task subAPI specification
168−-- taskAPI :: Swagger
196+-- taskAPI :: OpenApi
169197--
170198-- \-\- while API specification is just a combination
171199-- \-\- of subAPIs' specifications
172−-- api :: Swagger
200+-- api :: OpenApi
173201-- api = accountAPI <> taskAPI
174202-- @
175203
176204-- $lens
177205--
206+-- Note: if you're working with the <https://hackage.haskell.org/package/optics optics> library, take a look at "Data.OpenApi.Optics".
207+--
178208-- Since @'Swagger'@ has a fairly complex structure, lenses and prisms are used
179209-- to work comfortably with it. In combination with @'Monoid'@ instances, lenses
180210-- make it fairly simple to construct/modify any part of the specification:
181211--
182212-- >>> :{
183−-- encode $ (mempty :: Swagger)
184−-- & definitions .~ [ ("User", mempty & type_ .~ SwaggerString) ]
213+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
214+-- & components . schemas .~ [ ("User", mempty & type_ ?~ OpenApiString) ]
185215-- & paths .~
186216-- [ ("/user", mempty & get ?~ (mempty
187−-- & produces ?~ MimeList ["application/json"]
188−-- & at 200 ?~ ("OK" & _Inline.schema ?~ Ref (Reference "User"))
217+-- & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User")))
189218-- & at 404 ?~ "User info not found")) ]
190219-- :}
191−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"produces\":[\"application/json\"],\"responses\":{\"404\":{\"description\":\"User info not found\"},\"200\":{\"schema\":{\"$ref\":\"#/definitions/User\"},\"description\":\"OK\"}}}}},\"definitions\":{\"User\":{\"type\":\"string\"}}}"
220+-- {
221+-- "components": {
222+-- "schemas": {
223+-- "User": {
224+-- "type": "string"
225+-- }
226+-- }
227+-- },
228+-- "info": {
229+-- "title": "",
230+-- "version": ""
231+-- },
232+-- "openapi": "3.0.0",
233+-- "paths": {
234+-- "/user": {
235+-- "get": {
236+-- "responses": {
237+-- "200": {
238+-- "content": {
239+-- "application/json": {
240+-- "schema": {
241+-- "$ref": "#/components/schemas/User"
242+-- }
243+-- }
244+-- },
245+-- "description": "OK"
246+-- },
247+-- "404": {
248+-- "description": "User info not found"
249+-- }
250+-- }
251+-- }
252+-- }
253+-- }
254+-- }
192255--
193256-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
194257-- which produces @application/json@ output. It should respond with code @200@ and body specified
@@ -200,30 +263,34 @@ import Data.Swagger.Internal
200263-- common field is @'description'@. Many components of a Swagger specification
201264-- can have descriptions, and you can use the same name for them:
202265--
203−-- >>> encode $ (mempty :: Response) & description .~ "No content"
204−-- "{\"description\":\"No content\"}"
266+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & description .~ "No content"
267+-- {
268+-- "description": "No content"
269+-- }
205270-- >>> :{
206−-- encode $ (mempty :: Schema)
207−-- & type_ .~ SwaggerBoolean
271+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
272+-- & type_ ?~ OpenApiBoolean
208273-- & description ?~ "To be or not to be"
209274-- :}
210−-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
211−--
212−-- @'ParamSchema'@ is basically the /base schema specification/ and many types contain it (see @'HasParamSchema'@).
213−-- So for convenience, all @'ParamSchema'@ fields are transitively made fields of the type that has it.
214−-- For example, you can use @'type_'@ to access @'SwaggerType'@ of @'Header'@ schema without having to use @'paramSchema'@:
215−--
216−-- >>> encode $ (mempty :: Header) & type_ .~ SwaggerNumber
217−-- "{\"type\":\"number\"}"
275+-- {
276+-- "description": "To be or not to be",
277+-- "type": "boolean"
278+-- }
218279--
219280-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
220281-- have direct access to it via @'at' code@. Example:
221282--
222283-- >>> :{
223−-- encode $ (mempty :: Operation)
284+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
224285-- & at 404 ?~ "Not found"
225286-- :}
226−-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
287+-- {
288+-- "responses": {
289+-- "404": {
290+-- "description": "Not found"
291+-- }
292+-- }
293+-- }
227294--
228295-- You might've noticed that @'type_'@ has an extra underscore in its name
229296-- compared to, say, @'description'@ field accessor.
@@ -239,7 +306,7 @@ import Data.Swagger.Internal
239306-- @'ParamSchema'@ and @'Schema'@ are the two core types for data model specification.
240307--
241308-- @'ParamSchema' t@ specifies all the common properties, available for every data schema.
242−-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'SwaggerType'@ and @'SwaggerItems'@).
309+-- The @t@ parameter imposes some restrictions on @type@ and @items@ properties (see @'OpenApiType'@ and @'OpenApiItems'@).
243310--
244311-- @'Schema'@ is used for request and response bodies and allows specifying objects
245312-- with properties in addition to what @'ParamSchema'@ provides.
@@ -270,65 +337,81 @@ import Data.Swagger.Internal
270337-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
271338-- >>> instance ToJSON Person
272339-- >>> instance ToSchema Person
273−-- >>> encode (Person "David" 28)
274−-- "{\"age\":28,\"name\":\"David\"}"
275−-- >>> encode $ toSchema (Proxy :: Proxy Person)
276−-- "{\"required\":[\"name\",\"age\"],\"properties\":{\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"}},\"type\":\"object\"}"
277−--
278−-- Please note that not all valid Haskell data types will have a proper swagger schema. For example while we can derive a
279−-- schema for basic enums like
280−--
281−-- >>> data SampleEnum = ChoiceOne | ChoiceTwo deriving Generic
282−-- >>> instance ToSchema SampleEnum
283−-- >>> instance ToJSON SampleEnum
284−--
285−-- and for sum types that have constructors with values
286−--
287−-- >>> data SampleSumType = ChoiceInt Int | ChoiceString String deriving Generic
288−-- >>> instance ToSchema SampleSumType
289−-- >>> instance ToJSON SampleSumType
290−--
291−-- we can not derive a valid schema for a mix of the above. The following will result in a type error
292−--
293−#if __GLASGOW_HASKELL__ < 800
294−-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
295−-- >>> instance ToSchema BadMixedType
296−-- ...
297−-- ... error:
298−-- ... • No instance for (Data.Swagger.Internal.TypeShape.CannotDeriveSchemaForMixedSumType
299−-- ... BadMixedType)
300−-- ... arising from a use of ‘Data.Swagger.Internal.Schema.$dmdeclareNamedSchema’
301−-- ... • In the expression:
302−-- ... Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
303−-- ... In an equation for ‘declareNamedSchema’:
304−-- ... declareNamedSchema
305−-- ... = Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
306−-- ... In the instance declaration for ‘ToSchema BadMixedType’
307−#else
308−-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
309−-- >>> instance ToSchema BadMixedType
310−-- ...
311−-- ... error:
312−-- ... • Cannot derive Generic-based Swagger Schema for BadMixedType
313−-- ... BadMixedType is a mixed sum type (has both unit and non-unit constructors).
314−-- ... Swagger does not have a good representation for these types.
315−-- ... Use genericDeclareNamedSchemaUnrestricted if you want to derive schema
316−-- ... that matches aeson's Generic-based toJSON,
317−-- ... but that's not supported by some Swagger tools.
318−-- ... • In the expression:
319−-- ... Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
320−-- ... In an equation for ‘declareNamedSchema’:
321−-- ... declareNamedSchema
322−-- ... = Data.Swagger.Internal.Schema.$dmdeclareNamedSchema @BadMixedType
323−-- ... In the instance declaration for ‘ToSchema BadMixedType’
324−#endif
325−--
326−-- We can use 'genericDeclareNamedSchemaUnrestricted' to try our best to represent this type as a Swagger Schema and match 'ToJSON':
327−--
328−-- >>> data BadMixedType = ChoiceBool Bool | JustTag deriving Generic
329−-- >>> instance ToSchema BadMixedType where declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
330−-- >>> instance ToJSON BadMixedType
331−--
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+-- }
332415
333416-- $manipulation
334417-- Sometimes you have to work with an imported or generated @'Swagger'@.
@@ -336,10 +419,10 @@ import Data.Swagger.Internal
336419-- for a type-level servant API.
337420--
338421-- Lenses and prisms can be used to manipulate such specification to add additional information, tags, extra responses, etc.
339−-- To facilitate common needs, @"Data.Swagger.Operation"@ module provides useful helpers.
422+-- To facilitate common needs, @"Data.OpenApi.Operation"@ module provides useful helpers.
340423
341424-- $validation
342425-- While @'ToParamSchema'@ and @'ToSchema'@ provide means to easily obtain schemas for Haskell types,
343426-- there is no static mechanism to ensure those instances correspond to the @'ToHttpApiData'@ or @'ToJSON'@ instances.
344427--
345−-- @"Data.Swagger.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.
428+-- @"Data.OpenApi.Schema.Validation"@ addresses @'ToJSON'@/@'ToSchema'@ validation.
src/Data/OpenApi/Aeson/Compat.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE CPP #-}
2+
3+module Data.OpenApi.Aeson.Compat where
4+
5+#if MIN_VERSION_aeson(2,0,0)
6+import Data.Aeson (Key)
7+import qualified Data.Aeson.Key as Key
8+import qualified Data.Aeson.KeyMap as KeyMap
9+#else
10+import qualified Data.HashMap.Strict as HM
11+#endif
12+import Data.Bifunctor (first)
13+import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
14+import qualified Data.Text as T
15+
16+#if MIN_VERSION_aeson(2,0,0)
17+deleteKey :: Key -> KeyMap.KeyMap v -> KeyMap.KeyMap v
18+deleteKey = KeyMap.delete
19+
20+objectToList :: KeyMap.KeyMap v -> [(Key, v)]
21+objectToList = KeyMap.toList
22+
23+objectKeys :: KeyMap.KeyMap v -> [T.Text]
24+objectKeys = map Key.toText . KeyMap.keys
25+
26+stringToKey :: String -> Key
27+stringToKey = Key.fromString
28+
29+keyToString :: Key -> String
30+keyToString = Key.toString
31+
32+keyToText :: Key -> T.Text
33+keyToText = Key.toText
34+
35+toInsOrdHashMap :: KeyMap.KeyMap v -> InsOrdHashMap.InsOrdHashMap T.Text v
36+toInsOrdHashMap = InsOrdHashMap.fromList . fmap (first Key.toText) . KeyMap.toList
37+
38+fromInsOrdHashMap :: InsOrdHashMap.InsOrdHashMap T.Text v -> KeyMap.KeyMap v
39+fromInsOrdHashMap = KeyMap.fromList . fmap (first Key.fromText) . InsOrdHashMap.toList
40+
41+lookupKey :: T.Text -> KeyMap.KeyMap v -> Maybe v
42+lookupKey = KeyMap.lookup . Key.fromText
43+
44+hasKey :: T.Text -> KeyMap.KeyMap a -> Bool
45+hasKey = KeyMap.member . Key.fromText
46+#else
47+deleteKey :: T.Text -> HM.HashMap T.Text v -> HM.HashMap T.Text v
48+deleteKey = HM.delete
49+
50+objectToList :: HM.HashMap T.Text v -> [(T.Text, v)]
51+objectToList = HM.toList
52+
53+objectKeys :: HM.HashMap T.Text v -> [T.Text]
54+objectKeys = HM.keys
55+
56+stringToKey :: String -> T.Text
57+stringToKey = T.pack
58+
59+keyToString :: T.Text -> String
60+keyToString = T.unpack
61+
62+keyToText :: T.Text -> T.Text
63+keyToText = id
64+
65+toInsOrdHashMap :: HM.HashMap T.Text v -> InsOrdHashMap.InsOrdHashMap T.Text v
66+toInsOrdHashMap = InsOrdHashMap.fromHashMap
67+
68+fromInsOrdHashMap :: InsOrdHashMap.InsOrdHashMap T.Text v -> HM.HashMap T.Text v
69+fromInsOrdHashMap = InsOrdHashMap.toHashMap
70+
71+lookupKey :: T.Text -> HM.HashMap T.Text v -> Maybe v
72+lookupKey = HM.lookup
73+
74+hasKey :: T.Text -> HM.HashMap T.Text a -> Bool
75+hasKey = HM.member
76+#endif
src/Data/Swagger/Declare.hs → src/Data/OpenApi/Declare.hsrenamed+5−9
@@ -4,12 +4,12 @@
44{-# LANGUAGE MultiParamTypeClasses #-}
55{-# LANGUAGE UndecidableInstances #-}
66-- |
7−-- Module: Data.Swagger.Declare
7+-- Module: Data.OpenApi.Declare
88-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
99-- Stability: experimental
1010--
1111-- Declare monad transformer and associated functions.
12−module Data.Swagger.Declare where
12+module Data.OpenApi.Declare where
1313
1414import Prelude ()
1515import Prelude.Compat
@@ -60,7 +60,7 @@ instance (Applicative m, Monad m, Monoid d) => Monad (DeclareT d m) where
6060 return (mappend d' d'', y)
6161
6262instance Monoid d => MonadTrans (DeclareT d) where
63− lift m = DeclareT (\_ -> (,) mempty `liftM` m)
63+ lift m = DeclareT (\_ -> (,) mempty <$> m)
6464
6565-- |
6666-- Definitions of @declare@ and @look@ must satisfy the following laws:
@@ -103,12 +103,12 @@ looks f = f <$> look
103103-- | Evaluate @'DeclareT' d m a@ computation,
104104-- ignoring new output @d@.
105105evalDeclareT :: Monad m => DeclareT d m a -> d -> m a
106−evalDeclareT (DeclareT f) d = snd `liftM` f d
106+evalDeclareT (DeclareT f) d = snd <$> f d
107107
108108-- | Execute @'DeclateT' d m a@ computation,
109109-- ignoring result and only producing new output @d@.
110110execDeclareT :: Monad m => DeclareT d m a -> d -> m d
111−execDeclareT (DeclareT f) d = fst `liftM` f d
111+execDeclareT (DeclareT f) d = fst <$> f d
112112
113113-- | Evaluate @'DeclareT' d m a@ computation,
114114-- starting with empty output history.
@@ -162,10 +162,6 @@ instance MonadDeclare d m => MonadDeclare d (IdentityT m) where
162162 declare = lift . declare
163163 look = lift look
164164
165−instance MonadDeclare d m => MonadDeclare d (ListT m) where
166− declare = lift . declare
167− look = lift look
168−
169165instance MonadDeclare d m => MonadDeclare d (MaybeT m) where
170166 declare = lift . declare
171167 look = lift look
src/Data/OpenApi/Internal.hsnew file+1619
@@ -0,0 +1,1619 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DeriveDataTypeable #-}
4+{-# LANGUAGE DeriveGeneric #-}
5+{-# LANGUAGE DeriveTraversable #-}
6+{-# LANGUAGE FlexibleContexts #-}
7+{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE GADTs #-}
9+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10+{-# LANGUAGE OverloadedStrings #-}
11+{-# LANGUAGE RecordWildCards #-}
12+{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE StandaloneDeriving #-}
14+{-# LANGUAGE TemplateHaskell #-}
15+{-# LANGUAGE TypeFamilies #-}
16+{-# LANGUAGE UndecidableInstances #-}
17+module Data.OpenApi.Internal where
18+
19+import Prelude ()
20+import Prelude.Compat
21+
22+import 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)
48+
49+import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
50+import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
51+
52+import Data.OpenApi.Aeson.Compat (deleteKey)
53+import Data.OpenApi.Internal.AesonUtils (AesonDefaultValue (..), HasSwaggerAesonOptions (..),
54+ mkSwaggerAesonOptions, saoAdditionalPairs, saoSubObject,
55+ sopSwaggerGenericParseJSON, sopSwaggerGenericToEncoding,
56+ sopSwaggerGenericToJSON, sopSwaggerGenericToJSONWithOpts)
57+import Data.OpenApi.Internal.Utils
58+import Generics.SOP.TH (deriveGeneric)
59+
60+-- $setup
61+-- >>> :seti -XDataKinds
62+-- >>> import Data.Aeson
63+-- >>> import Data.ByteString.Lazy.Char8 as BSL
64+-- >>> import Data.OpenApi.Internal.Utils
65+
66+-- | A list of definitions that can be used in references.
67+type Definitions = InsOrdHashMap Text
68+
69+-- | This is the root document object for the API specification.
70+data OpenApi = OpenApi
71+ { -- | Provides metadata about the API.
72+ -- The metadata can be used by the clients if needed.
73+ _openApiInfo :: Info
74+
75+ -- | An array of Server Objects, which provide connectivity information
76+ -- to a target server. If the servers property is not provided, or is an empty array,
77+ -- the default value would be a 'Server' object with a url value of @/@.
78+ , _openApiServers :: [Server]
79+
80+ -- | The available paths and operations for the API.
81+ , _openApiPaths :: InsOrdHashMap FilePath PathItem
82+
83+ -- | An element to hold various schemas for the specification.
84+ , _openApiComponents :: Components
85+
86+ -- | A declaration of which security mechanisms can be used across the API.
87+ -- The list of values includes alternative security requirement objects that can be used.
88+ -- Only one of the security requirement objects need to be satisfied to authorize a request.
89+ -- Individual operations can override this definition.
90+ -- To make security optional, an empty security requirement can be included in the array.
91+ , _openApiSecurity :: [SecurityRequirement]
92+
93+ -- | A list of tags used by the specification with additional metadata.
94+ -- The order of the tags can be used to reflect on their order by the parsing tools.
95+ -- Not all tags that are used by the 'Operation' Object must be declared.
96+ -- The tags that are not declared MAY be organized randomly or based on the tools' logic.
97+ -- Each tag name in the list MUST be unique.
98+ , _openApiTags :: InsOrdHashSet Tag
99+
100+ -- | Additional external documentation.
101+ , _openApiExternalDocs :: Maybe ExternalDocs
102+ } deriving (Eq, Show, Generic, Data, Typeable)
103+
104+-- | The object provides metadata about the API.
105+-- The metadata MAY be used by the clients if needed,
106+-- and MAY be presented in editing or documentation generation tools for convenience.
107+data Info = Info
108+ { -- | The title of the API.
109+ _infoTitle :: Text
110+
111+ -- | A short description of the API.
112+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
113+ , _infoDescription :: Maybe Text
114+
115+ -- | A URL to the Terms of Service for the API. MUST be in the format of a URL.
116+ , _infoTermsOfService :: Maybe Text
117+
118+ -- | The contact information for the exposed API.
119+ , _infoContact :: Maybe Contact
120+
121+ -- | The license information for the exposed API.
122+ , _infoLicense :: Maybe License
123+
124+ -- | The version of the OpenAPI document (which is distinct from the
125+ -- OpenAPI Specification version or the API implementation version).
126+ , _infoVersion :: Text
127+ } deriving (Eq, Show, Generic, Data, Typeable)
128+
129+-- | Contact information for the exposed API.
130+data Contact = Contact
131+ { -- | The identifying name of the contact person/organization.
132+ _contactName :: Maybe Text
133+
134+ -- | The URL pointing to the contact information.
135+ , _contactUrl :: Maybe URL
136+
137+ -- | The email address of the contact person/organization.
138+ , _contactEmail :: Maybe Text
139+ } deriving (Eq, Show, Generic, Data, Typeable)
140+
141+-- | License information for the exposed API.
142+data License = License
143+ { -- | The license name used for the API.
144+ _licenseName :: Text
145+
146+ -- | A URL to the license used for the API.
147+ , _licenseUrl :: Maybe URL
148+ } deriving (Eq, Show, Generic, Data, Typeable)
149+
150+instance IsString License where
151+ fromString s = License (fromString s) Nothing
152+
153+-- | An object representing a Server.
154+data Server = Server
155+ { -- | A URL to the target host. This URL supports Server Variables and MAY be relative,
156+ -- to indicate that the host location is relative to the location where
157+ -- the OpenAPI document is being served. Variable substitutions will be made when
158+ -- a variable is named in @{brackets}@.
159+ _serverUrl :: Text
160+
161+ -- | An optional string describing the host designated by the URL.
162+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
163+ , _serverDescription :: Maybe Text
164+
165+ -- | A map between a variable name and its value.
166+ -- The value is used for substitution in the server's URL template.
167+ , _serverVariables :: InsOrdHashMap Text ServerVariable
168+ } deriving (Eq, Show, Generic, Data, Typeable)
169+
170+data ServerVariable = ServerVariable
171+ { -- | An enumeration of string values to be used if the substitution options
172+ -- are from a limited set. The array SHOULD NOT be empty.
173+ _serverVariableEnum :: Maybe (InsOrdHashSet Text) -- TODO NonEmpty
174+
175+ -- | The default value to use for substitution, which SHALL be sent if an alternate value
176+ -- is not supplied. Note this behavior is different than the 'Schema\ Object's treatment
177+ -- of default values, because in those cases parameter values are optional.
178+ -- If the '_serverVariableEnum' is defined, the value SHOULD exist in the enum's values.
179+ , _serverVariableDefault :: Text
180+
181+ -- | An optional description for the server variable.
182+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
183+ , _serverVariableDescription :: Maybe Text
184+ } deriving (Eq, Show, Generic, Data, Typeable)
185+
186+instance IsString Server where
187+ fromString s = Server (fromString s) Nothing mempty
188+
189+-- | Holds a set of reusable objects for different aspects of the OAS.
190+-- All objects defined within the components object will have no effect on the API
191+-- unless they are explicitly referenced from properties outside the components object.
192+data Components = Components
193+ { _componentsSchemas :: Definitions Schema
194+ , _componentsResponses :: Definitions Response
195+ , _componentsParameters :: Definitions Param
196+ , _componentsExamples :: Definitions Example
197+ , _componentsRequestBodies :: Definitions RequestBody
198+ , _componentsHeaders :: Definitions Header
199+ , _componentsSecuritySchemes :: SecurityDefinitions
200+ , _componentsLinks :: Definitions Link
201+ , _componentsCallbacks :: Definitions Callback
202+ } deriving (Eq, Show, Generic, Data, Typeable)
203+
204+-- | Describes the operations available on a single path.
205+-- A @'PathItem'@ may be empty, due to ACL constraints.
206+-- The path itself is still exposed to the documentation viewer
207+-- but they will not know which operations and parameters are available.
208+data PathItem = PathItem
209+ { -- | An optional, string summary, intended to apply to all operations in this path.
210+ _pathItemSummary :: Maybe Text
211+
212+ -- | An optional, string description, intended to apply to all operations in this path.
213+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
214+ , _pathItemDescription :: Maybe Text
215+
216+ -- | A definition of a GET operation on this path.
217+ , _pathItemGet :: Maybe Operation
218+
219+ -- | A definition of a PUT operation on this path.
220+ , _pathItemPut :: Maybe Operation
221+
222+ -- | A definition of a POST operation on this path.
223+ , _pathItemPost :: Maybe Operation
224+
225+ -- | A definition of a DELETE operation on this path.
226+ , _pathItemDelete :: Maybe Operation
227+
228+ -- | A definition of a OPTIONS operation on this path.
229+ , _pathItemOptions :: Maybe Operation
230+
231+ -- | A definition of a HEAD operation on this path.
232+ , _pathItemHead :: Maybe Operation
233+
234+ -- | A definition of a PATCH operation on this path.
235+ , _pathItemPatch :: Maybe Operation
236+
237+ -- | A definition of a TRACE operation on this path.
238+ , _pathItemTrace :: Maybe Operation
239+
240+ -- | An alternative server array to service all operations in this path.
241+ , _pathItemServers :: [Server]
242+
243+ -- | A list of parameters that are applicable for all the operations described under this path.
244+ -- These parameters can be overridden at the operation level, but cannot be removed there.
245+ -- The list MUST NOT include duplicated parameters.
246+ -- A unique parameter is defined by a combination of a name and location.
247+ , _pathItemParameters :: [Referenced Param]
248+ } deriving (Eq, Show, Generic, Data, Typeable)
249+
250+-- | Describes a single API operation on a path.
251+data Operation = Operation
252+ { -- | A list of tags for API documentation control.
253+ -- Tags can be used for logical grouping of operations by resources or any other qualifier.
254+ _operationTags :: InsOrdHashSet TagName
255+
256+ -- | A short summary of what the operation does.
257+ -- For maximum readability in the swagger-ui, this field SHOULD be less than 120 characters.
258+ , _operationSummary :: Maybe Text
259+
260+ -- | A verbose explanation of the operation behavior.
261+ -- [CommonMark syntax](https://spec.commonmark.org/) can be used for rich text representation.
262+ , _operationDescription :: Maybe Text
263+
264+ -- | Additional external documentation for this operation.
265+ , _operationExternalDocs :: Maybe ExternalDocs
266+
267+ -- | Unique string used to identify the operation.
268+ -- The id MUST be unique among all operations described in the API.
269+ -- The operationId value is **case-sensitive**.
270+ -- Tools and libraries MAY use the operationId to uniquely identify an operation, therefore,
271+ -- it is RECOMMENDED to follow common programming naming conventions.
272+ , _operationOperationId :: Maybe Text
273+
274+ -- | A list of parameters that are applicable for this operation.
275+ -- If a parameter is already defined at the @'PathItem'@,
276+ -- the new definition will override it, but can never remove it.
277+ -- The list MUST NOT include duplicated parameters.
278+ -- A unique parameter is defined by a combination of a name and location.
279+ , _operationParameters :: [Referenced Param]
280+
281+ -- | The request body applicable for this operation.
282+ -- The requestBody is only supported in HTTP methods where the HTTP 1.1
283+ -- specification [RFC7231](https://tools.ietf.org/html/rfc7231#section-4.3.1)
284+ -- has explicitly defined semantics for request bodies.
285+ -- In other cases where the HTTP spec is vague, requestBody SHALL be ignored by consumers.
286+ , _operationRequestBody :: Maybe (Referenced RequestBody)
287+
288+ -- | The list of possible responses as they are returned from executing this operation.
289+ , _operationResponses :: Responses
290+
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)
296+
297+ -- | Declares this operation to be deprecated.
298+ -- Usage of the declared operation should be refrained.
299+ -- Default value is @False@.
300+ , _operationDeprecated :: Maybe Bool
301+
302+ -- | A declaration of which security schemes are applied for this operation.
303+ -- The list of values describes alternative security schemes that can be used
304+ -- (that is, there is a logical OR between the security requirements).
305+ -- This definition overrides any declared top-level security.
306+ -- To remove a top-level security declaration, @Just []@ can be used.
307+ , _operationSecurity :: [SecurityRequirement]
308+
309+ -- | An alternative server array to service this operation.
310+ -- If an alternative server object is specified at the 'PathItem' Object or Root level,
311+ -- it will be overridden by this value.
312+ , _operationServers :: [Server]
313+ } deriving (Eq, Show, Generic, Data, Typeable)
314+
315+-- This instance should be in @http-media@.
316+instance Data MediaType where
317+ gunfold k z c = case constrIndex c of
318+ 1 -> k (k (k (z (\main sub params -> foldl (/:) (main // sub) (Map.toList params)))))
319+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MediaType."
320+
321+ toConstr _ = mediaTypeConstr
322+
323+ dataTypeOf _ = mediaTypeData
324+
325+mediaTypeConstr = mkConstr mediaTypeData "MediaType" [] Prefix
326+mediaTypeData = mkDataType "MediaType" [mediaTypeConstr]
327+
328+instance Hashable MediaType where
329+ hashWithSalt salt mt = salt `hashWithSalt` show mt
330+
331+-- | Describes a single request body.
332+data RequestBody = RequestBody
333+ { -- | A brief description of the request body. This could contain examples of use.
334+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
335+ _requestBodyDescription :: Maybe Text
336+
337+ -- | The content of the request body.
338+ -- The key is a media type or media type range and the value describes it.
339+ -- 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
342+
343+ -- | Determines if the request body is required in the request.
344+ -- Defaults to 'False'.
345+ , _requestBodyRequired :: Maybe Bool
346+ } deriving (Eq, Show, Generic, Data, Typeable)
347+
348+-- | Each Media Type Object provides schema and examples for the media type identified by its key.
349+data MediaTypeObject = MediaTypeObject
350+ { _mediaTypeObjectSchema :: Maybe (Referenced Schema)
351+
352+ -- | Example of the media type.
353+ -- The example object SHOULD be in the correct format as specified by the media type.
354+ , _mediaTypeObjectExample :: Maybe Value
355+
356+ -- | Examples of the media type.
357+ -- Each example object SHOULD match the media type and specified schema if present.
358+ , _mediaTypeObjectExamples :: InsOrdHashMap Text (Referenced Example)
359+
360+ -- | A map between a property name and its encoding information.
361+ -- The key, being the property name, MUST exist in the schema as a property.
362+ -- The encoding object SHALL only apply to 'RequestBody' objects when the media type
363+ -- is @multipart@ or @application/x-www-form-urlencoded@.
364+ , _mediaTypeObjectEncoding :: InsOrdHashMap Text Encoding
365+ } deriving (Eq, Show, Generic, Data, Typeable)
366+
367+-- | In order to support common ways of serializing simple parameters, a set of style values are defined.
368+data Style
369+ = StyleMatrix
370+ -- ^ Path-style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
371+ | StyleLabel
372+ -- ^ Label style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
373+ | StyleForm
374+ -- ^ Form style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
375+ -- This option replaces @collectionFormat@ with a @csv@ (when @explode@ is false) or @multi@
376+ -- (when explode is true) value from OpenAPI 2.0.
377+ | StyleSimple
378+ -- ^ Simple style parameters defined by [RFC6570](https://tools.ietf.org/html/rfc6570#section-3.2.7).
379+ -- This option replaces @collectionFormat@ with a @csv@ value from OpenAPI 2.0.
380+ | StyleSpaceDelimited
381+ -- ^ Space separated array values.
382+ -- This option replaces @collectionFormat@ equal to @ssv@ from OpenAPI 2.0.
383+ | StylePipeDelimited
384+ -- ^ Pipe separated array values.
385+ -- This option replaces @collectionFormat@ equal to @pipes@ from OpenAPI 2.0.
386+ | StyleDeepObject
387+ -- ^ Provides a simple way of rendering nested objects using form parameters.
388+ deriving (Eq, Show, Generic, Data, Typeable)
389+
390+data Encoding = Encoding
391+ { -- | The Content-Type for encoding a specific property.
392+ -- Default value depends on the property type: for @string@
393+ -- with format being @binary@ – @application/octet-stream@;
394+ -- for other primitive types – @text/plain@; for object - @application/json@;
395+ -- for array – the default is defined based on the inner type.
396+ -- 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
399+
400+ -- | A map allowing additional information to be provided as headers,
401+ -- for example @Content-Disposition@. @Content-Type@ is described separately
402+ -- and SHALL be ignored in this section.
403+ -- This property SHALL be ignored if the request body media type is not a @multipart@.
404+ , _encodingHeaders :: InsOrdHashMap Text (Referenced Header)
405+
406+ -- | Describes how a specific property value will be serialized depending on its type.
407+ -- See 'Param' Object for details on the style property.
408+ -- The behavior follows the same values as query parameters, including default values.
409+ -- This property SHALL be ignored if the request body media type
410+ -- is not @application/x-www-form-urlencoded@.
411+ , _encodingStyle :: Maybe Style
412+
413+ -- | When this is true, property values of type @array@ or @object@ generate
414+ -- separate parameters for each value of the array,
415+ -- or key-value-pair of the map.
416+ -- For other types of properties this property has no effect.
417+ -- When style is form, the default value is @true@. For all other styles,
418+ -- the default value is @false@. This property SHALL be ignored
419+ -- if the request body media type is not @application/x-www-form-urlencoded@.
420+ , _encodingExplode :: Maybe Bool
421+
422+ -- | Determines whether the parameter value SHOULD allow reserved characters,
423+ -- as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
424+ -- @:/?#[]@!$&'()*+,;=@ to be included without percent-encoding.
425+ -- The default value is @false@. This property SHALL be ignored if the request body media type
426+ -- is not @application/x-www-form-urlencoded@.
427+ , _encodingAllowReserved :: Maybe Bool
428+ } deriving (Eq, Show, Generic, Data, Typeable)
429+
430+newtype MimeList = MimeList { getMimeList :: [MediaType] }
431+ deriving (Eq, Show, Semigroup, Monoid, Typeable)
432+
433+mimeListConstr :: Constr
434+mimeListConstr = mkConstr mimeListDataType "MimeList" ["getMimeList"] Prefix
435+
436+mimeListDataType :: DataType
437+mimeListDataType = mkDataType "Data.OpenApi.MimeList" [mimeListConstr]
438+
439+instance Data MimeList where
440+ gunfold k z c = case constrIndex c of
441+ 1 -> k (z (MimeList . map fromString))
442+ _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MimeList."
443+ toConstr (MimeList _) = mimeListConstr
444+ dataTypeOf _ = mimeListDataType
445+
446+-- | Describes a single operation parameter.
447+-- A unique parameter is defined by a combination of a name and location.
448+data Param = Param
449+ { -- | The name of the parameter.
450+ -- Parameter names are case sensitive.
451+ _paramName :: Text
452+
453+ -- | A brief description of the parameter.
454+ -- This could contain examples of use.
455+ -- CommonMark syntax MAY be used for rich text representation.
456+ , _paramDescription :: Maybe Text
457+
458+ -- | Determines whether this parameter is mandatory.
459+ -- If the parameter is in "path", this property is required and its value MUST be true.
460+ -- Otherwise, the property MAY be included and its default value is @False@.
461+ , _paramRequired :: Maybe Bool
462+
463+ -- | Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
464+ -- Default value is @false@.
465+ , _paramDeprecated :: Maybe Bool
466+
467+ -- | The location of the parameter.
468+ , _paramIn :: ParamLocation
469+
470+ -- | 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
481+
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)
517+
518+data Example = Example
519+ { -- | Short description for the example.
520+ _exampleSummary :: Maybe Text
521+
522+ -- | Long description for the example.
523+ -- CommonMark syntax MAY be used for rich text representation.
524+ , _exampleDescription :: Maybe Text
525+
526+ -- | Embedded literal example.
527+ -- The '_exampleValue' field and '_exampleExternalValue' field are mutually exclusive.
528+ --
529+ -- To represent examples of media types that cannot naturally represented in JSON or YAML,
530+ -- use a string value to contain the example, escaping where necessary.
531+ , _exampleValue :: Maybe Value
532+
533+ -- | A URL that points to the literal example.
534+ -- This provides the capability to reference examples that cannot easily be included
535+ -- in JSON or YAML documents. The '_exampleValue' field
536+ -- and '_exampleExternalValue' field are mutually exclusive.
537+ , _exampleExternalValue :: Maybe URL
538+ } deriving (Eq, Show, Generic, Typeable, Data)
539+
540+data ExpressionOrValue
541+ = Expression Text
542+ | Value Value
543+ deriving (Eq, Show, Generic, Typeable, Data)
544+
545+-- | The Link object represents a possible design-time link for a response.
546+-- The presence of a link does not guarantee the caller's ability to successfully invoke it,
547+-- rather it provides a known relationship and traversal mechanism between responses and other operations.
548+data Link = Link
549+ { -- | A relative or absolute URI reference to an OAS operation.
550+ -- This field is mutually exclusive of the '_linkOperationId' field,
551+ -- and MUST point to an 'Operation' Object. Relative '_linkOperationRef'
552+ -- values MAY be used to locate an existing 'Operation' Object in the OpenAPI definition.
553+ _linkOperationRef :: Maybe Text
554+
555+ -- | The name of an /existing/, resolvable OAS operation, as defined with a unique
556+ -- '_operationOperationId'. This field is mutually exclusive of the '_linkOperationRef' field.
557+ , _linkOperationId :: Maybe Text
558+
559+ -- | A map representing parameters to pass to an operation as specified with '_linkOperationId'
560+ -- or identified via '_linkOperationRef'. The key is the parameter name to be used, whereas
561+ -- the value can be a constant or an expression to be evaluated and passed to the linked operation.
562+ -- The parameter name can be qualified using the parameter location @[{in}.]{name}@
563+ -- for operations that use the same parameter name in different locations (e.g. path.id).
564+ , _linkParameters :: InsOrdHashMap Text ExpressionOrValue
565+
566+ -- | A literal value or @{expression}@ to use as a request body when calling the target operation.
567+ , _linkRequestBody :: Maybe ExpressionOrValue
568+
569+ -- | A description of the link.
570+ , _linkDescription :: Maybe Text
571+
572+ -- | A server object to be used by the target operation.
573+ , _linkServer :: Maybe Server
574+ } deriving (Eq, Show, Generic, Typeable, Data)
575+
576+-- | Items for @'OpenApiArray'@ schemas.
577+--
578+-- __Warning__: OpenAPI 3.0 does not support tuple arrays. However, OpenAPI 3.1 will, as
579+-- it will incorporate Json Schema mostly verbatim.
580+--
581+-- @'OpenApiItemsObject'@ should be used to specify homogenous array @'Schema'@s.
582+--
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)
598+
599+data ParamLocation
600+ = -- | Parameters that are appended to the URL.
601+ -- For example, in @/items?id=###@, the query parameter is @id@.
602+ ParamQuery
603+ -- | Custom headers that are expected as part of the request.
604+ | ParamHeader
605+ -- | Used together with Path Templating, where the parameter value is actually part of the operation's URL.
606+ -- This does not include the host or base path of the API.
607+ -- For example, in @/items/{itemId}@, the path parameter is @itemId@.
608+ | ParamPath
609+ -- | Used to pass a specific cookie value to the API.
610+ | ParamCookie
611+ deriving (Eq, Show, Generic, Data, Typeable)
612+
613+type Format = Text
614+
615+type ParamName = Text
616+
617+data Schema = Schema
618+ { _schemaTitle :: Maybe Text
619+ , _schemaDescription :: Maybe Text
620+ , _schemaRequired :: [ParamName]
621+
622+ , _schemaNullable :: Maybe Bool
623+ , _schemaAllOf :: Maybe [Referenced Schema]
624+ , _schemaOneOf :: Maybe [Referenced Schema]
625+ , _schemaNot :: Maybe (Referenced Schema)
626+ , _schemaAnyOf :: Maybe [Referenced Schema]
627+ , _schemaProperties :: InsOrdHashMap Text (Referenced Schema)
628+ , _schemaAdditionalProperties :: Maybe AdditionalProperties
629+
630+ , _schemaDiscriminator :: Maybe Discriminator
631+ , _schemaReadOnly :: Maybe Bool
632+ , _schemaWriteOnly :: Maybe Bool
633+ , _schemaXml :: Maybe Xml
634+ , _schemaExternalDocs :: Maybe ExternalDocs
635+ , _schemaExample :: Maybe Value
636+ , _schemaDeprecated :: Maybe Bool
637+
638+ , _schemaMaxProperties :: Maybe Integer
639+ , _schemaMinProperties :: Maybe Integer
640+
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
663+ } deriving (Eq, Show, Generic, Data, Typeable)
664+
665+-- | Regex pattern for @string@ type.
666+type Pattern = Text
667+
668+data Discriminator = Discriminator
669+ { -- | The name of the property in the payload that will hold the discriminator value.
670+ _discriminatorPropertyName :: Text
671+
672+ -- | An object to hold mappings between payload values and schema names or references.
673+ , _discriminatorMapping :: InsOrdHashMap Text Text
674+ } deriving (Eq, Show, Generic, Data, Typeable)
675+
676+-- | A @'Schema'@ with an optional name.
677+-- This name can be used in references.
678+data NamedSchema = NamedSchema
679+ { _namedSchemaName :: Maybe Text
680+ , _namedSchemaSchema :: Schema
681+ } deriving (Eq, Show, Generic, Data, Typeable)
682+
683+data Xml = Xml
684+ { -- | 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.
686+ -- When defined alongside type being array (outside the items),
687+ -- it will affect the wrapping element and only if wrapped is true.
688+ -- If wrapped is false, it will be ignored.
689+ _xmlName :: Maybe Text
690+
691+ -- | The URL of the namespace definition.
692+ -- Value SHOULD be in the form of a URL.
693+ , _xmlNamespace :: Maybe Text
694+
695+ -- | The prefix to be used for the name.
696+ , _xmlPrefix :: Maybe Text
697+
698+ -- | Declares whether the property definition translates to an attribute instead of an element.
699+ -- Default value is @False@.
700+ , _xmlAttribute :: Maybe Bool
701+
702+ -- | MAY be used only for an array definition.
703+ -- Signifies whether the array is wrapped
704+ -- (for example, @\<books\>\<book/\>\<book/\>\</books\>@)
705+ -- or unwrapped (@\<book/\>\<book/\>@).
706+ -- Default value is @False@.
707+ -- The definition takes effect only when defined alongside type being array (outside the items).
708+ , _xmlWrapped :: Maybe Bool
709+ } deriving (Eq, Show, Generic, Data, Typeable)
710+
711+-- | A container for the expected responses of an operation.
712+-- The container maps a HTTP response code to the expected response.
713+-- It is not expected from the documentation to necessarily cover all possible HTTP response codes,
714+-- since they may not be known in advance.
715+-- However, it is expected from the documentation to cover a successful operation response and any known errors.
716+data Responses = Responses
717+ { -- | The documentation of responses other than the ones declared for specific HTTP response codes.
718+ -- It can be used to cover undeclared responses.
719+ _responsesDefault :: Maybe (Referenced Response)
720+
721+ -- | Any HTTP status code can be used as the property name (one property per HTTP status code).
722+ -- Describes the expected response for those HTTP status codes.
723+ , _responsesResponses :: InsOrdHashMap HttpStatusCode (Referenced Response)
724+ } deriving (Eq, Show, Generic, Data, Typeable)
725+
726+type HttpStatusCode = Int
727+
728+-- | Describes a single response from an API Operation.
729+data Response = Response
730+ { -- | A short description of the response.
731+ -- [CommonMark syntax](https://spec.commonmark.org/) can be used for rich text representation.
732+ _responseDescription :: Text
733+
734+ -- | A map containing descriptions of potential response payloads.
735+ -- The key is a media type or media type range and the value describes it.
736+ -- 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
739+
740+ -- | Maps a header name to its definition.
741+ , _responseHeaders :: InsOrdHashMap HeaderName (Referenced Header)
742+
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)
747+ } deriving (Eq, Show, Generic, Data, Typeable)
748+
749+instance 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)
759+
760+type HeaderName = Text
761+
762+-- | Header fields have the same meaning as for 'Param'.
763+--
764+-- Style is always treated as 'StyleSimple', as it is the only value allowed for headers.
765+data Header = Header
766+ { -- | 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)
775+
776+ , _headerSchema :: Maybe (Referenced Schema)
777+ } deriving (Eq, Show, Generic, Data, Typeable)
778+
779+-- | The location of the API key.
780+data ApiKeyLocation
781+ = ApiKeyQuery
782+ | ApiKeyHeader
783+ | ApiKeyCookie
784+ deriving (Eq, Show, Generic, Data, Typeable)
785+
786+data ApiKeyParams = ApiKeyParams
787+ { -- | The name of the header or query parameter to be used.
788+ _apiKeyName :: Text
789+
790+ -- | The location of the API key.
791+ , _apiKeyIn :: ApiKeyLocation
792+ } deriving (Eq, Show, Generic, Data, Typeable)
793+
794+-- | The authorization URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
795+type AuthorizationURL = Text
796+
797+-- | The token URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
798+type TokenURL = Text
799+
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}
810+ deriving (Eq, Show, Generic, Data, Typeable)
811+
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
822+
823+ -- | 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)
841+ } deriving (Eq, Show, Generic, Data, Typeable)
842+
843+type BearerFormat = Text
844+
845+data HttpSchemeType
846+ = HttpSchemeBearer (Maybe BearerFormat)
847+ | HttpSchemeBasic
848+ | HttpSchemeCustom Text
849+ deriving (Eq, Show, Generic, Data, Typeable)
850+
851+-- |
852+--
853+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer Nothing))
854+-- {
855+-- "scheme": "bearer",
856+-- "type": "http"
857+-- }
858+--
859+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer (Just "jwt")))
860+-- {
861+-- "bearerFormat": "jwt",
862+-- "scheme": "bearer",
863+-- "type": "http"
864+-- }
865+--
866+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp HttpSchemeBasic)
867+-- {
868+-- "scheme": "basic",
869+-- "type": "http"
870+-- }
871+--
872+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeCustom "CANARY"))
873+-- {
874+-- "scheme": "CANARY",
875+-- "type": "http"
876+-- }
877+--
878+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeApiKey (ApiKeyParams "id" ApiKeyCookie))
879+-- {
880+-- "in": "cookie",
881+-- "name": "id",
882+-- "type": "apiKey"
883+-- }
884+--
885+data SecuritySchemeType
886+ = SecuritySchemeHttp HttpSchemeType
887+ | SecuritySchemeApiKey ApiKeyParams
888+ | SecuritySchemeOAuth2 OAuth2Flows
889+ | SecuritySchemeOpenIdConnect URL
890+ deriving (Eq, Show, Generic, Data, Typeable)
891+
892+data SecurityScheme = SecurityScheme
893+ { -- | The type of the security scheme.
894+ _securitySchemeType :: SecuritySchemeType
895+
896+ -- | A short description for security scheme.
897+ , _securitySchemeDescription :: Maybe Text
898+ } deriving (Eq, Show, Generic, Data, Typeable)
899+
900+newtype SecurityDefinitions
901+ = SecurityDefinitions (Definitions SecurityScheme)
902+ deriving (Eq, Show, Generic, Data, Typeable)
903+
904+-- | Lists the required security schemes to execute this operation.
905+-- The object can have multiple security schemes declared in it which are all required
906+-- (that is, there is a logical AND between the schemes).
907+newtype SecurityRequirement = SecurityRequirement
908+ { getSecurityRequirement :: InsOrdHashMap Text [Text]
909+ } deriving (Eq, Read, Show, Semigroup, Monoid, ToJSON, FromJSON, Data, Typeable)
910+
911+-- | Tag name.
912+type TagName = Text
913+
914+-- | Allows adding meta data to a single tag that is used by @Operation@.
915+-- It is not mandatory to have a @Tag@ per tag used there.
916+data Tag = Tag
917+ { -- | The name of the tag.
918+ _tagName :: TagName
919+
920+ -- | A short description for the tag.
921+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
922+ , _tagDescription :: Maybe Text
923+
924+ -- | Additional external documentation for this tag.
925+ , _tagExternalDocs :: Maybe ExternalDocs
926+ } deriving (Eq, Ord, Show, Generic, Data, Typeable)
927+
928+instance Hashable Tag
929+
930+instance IsString Tag where
931+ fromString s = Tag (fromString s) Nothing Nothing
932+
933+-- | Allows referencing an external resource for extended documentation.
934+data ExternalDocs = ExternalDocs
935+ { -- | A short description of the target documentation.
936+ -- [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
937+ _externalDocsDescription :: Maybe Text
938+
939+ -- | The URL for the target documentation.
940+ , _externalDocsUrl :: URL
941+ } deriving (Eq, Ord, Show, Generic, Data, Typeable)
942+
943+instance Hashable ExternalDocs
944+
945+-- | A simple object to allow referencing other definitions in the specification.
946+-- It can be used to reference parameters and responses that are defined at the top level for reuse.
947+newtype Reference = Reference { getReference :: Text }
948+ deriving (Eq, Show, Data, Typeable)
949+
950+data Referenced a
951+ = Ref Reference
952+ | Inline a
953+ deriving (Eq, Show, Functor, Data, Typeable)
954+
955+instance IsString a => IsString (Referenced a) where
956+ fromString = Inline . fromString
957+
958+newtype URL = URL { getUrl :: Text } deriving (Eq, Ord, Show, Hashable, ToJSON, FromJSON, Data, Typeable)
959+
960+data AdditionalProperties
961+ = AdditionalPropertiesAllowed Bool
962+ | AdditionalPropertiesSchema (Referenced Schema)
963+ deriving (Eq, Show, Data, Typeable)
964+
965+-------------------------------------------------------------------------------
966+-- Generic instances
967+-------------------------------------------------------------------------------
968+
969+deriveGeneric ''Server
970+deriveGeneric ''Components
971+deriveGeneric ''Header
972+deriveGeneric ''OAuth2Flow
973+deriveGeneric ''OAuth2Flows
974+deriveGeneric ''Operation
975+deriveGeneric ''Param
976+deriveGeneric ''PathItem
977+deriveGeneric ''Response
978+deriveGeneric ''RequestBody
979+deriveGeneric ''MediaTypeObject
980+deriveGeneric ''Responses
981+deriveGeneric ''SecurityScheme
982+deriveGeneric ''Schema
983+deriveGeneric ''OpenApi
984+deriveGeneric ''Example
985+deriveGeneric ''Encoding
986+deriveGeneric ''Link
987+
988+-- =======================================================================
989+-- Monoid instances
990+-- =======================================================================
991+
992+instance Semigroup OpenApi where
993+ (<>) = genericMappend
994+instance Monoid OpenApi where
995+ mempty = genericMempty
996+ mappend = (<>)
997+
998+instance Semigroup Info where
999+ (<>) = genericMappend
1000+instance Monoid Info where
1001+ mempty = genericMempty
1002+ mappend = (<>)
1003+
1004+instance Semigroup Contact where
1005+ (<>) = genericMappend
1006+instance Monoid Contact where
1007+ mempty = genericMempty
1008+ mappend = (<>)
1009+
1010+instance Semigroup Components where
1011+ (<>) = genericMappend
1012+instance Monoid Components where
1013+ mempty = genericMempty
1014+ mappend = (<>)
1015+
1016+instance Semigroup PathItem where
1017+ (<>) = genericMappend
1018+instance Monoid PathItem where
1019+ mempty = genericMempty
1020+ mappend = (<>)
1021+
1022+instance Semigroup Schema where
1023+ (<>) = genericMappend
1024+instance Monoid Schema where
1025+ mempty = genericMempty
1026+ mappend = (<>)
1027+
1028+instance Semigroup Param where
1029+ (<>) = genericMappend
1030+instance Monoid Param where
1031+ mempty = genericMempty
1032+ mappend = (<>)
1033+
1034+instance Semigroup Header where
1035+ (<>) = genericMappend
1036+instance Monoid Header where
1037+ mempty = genericMempty
1038+ mappend = (<>)
1039+
1040+instance Semigroup Responses where
1041+ (<>) = genericMappend
1042+instance Monoid Responses where
1043+ mempty = genericMempty
1044+ mappend = (<>)
1045+
1046+instance Semigroup Response where
1047+ (<>) = genericMappend
1048+instance Monoid Response where
1049+ mempty = genericMempty
1050+ mappend = (<>)
1051+
1052+instance Semigroup MediaTypeObject where
1053+ (<>) = genericMappend
1054+instance Monoid MediaTypeObject where
1055+ mempty = genericMempty
1056+ mappend = (<>)
1057+
1058+instance Semigroup Encoding where
1059+ (<>) = genericMappend
1060+instance Monoid Encoding where
1061+ mempty = genericMempty
1062+ mappend = (<>)
1063+
1064+instance Semigroup ExternalDocs where
1065+ (<>) = genericMappend
1066+instance Monoid ExternalDocs where
1067+ mempty = genericMempty
1068+ mappend = (<>)
1069+
1070+instance Semigroup Operation where
1071+ (<>) = genericMappend
1072+instance Monoid Operation where
1073+ mempty = genericMempty
1074+ mappend = (<>)
1075+
1076+instance Semigroup (OAuth2Flow p) where
1077+ l@OAuth2Flow{ _oAath2RefreshUrl = lUrl, _oAuth2Scopes = lScopes }
1078+ <> OAuth2Flow { _oAath2RefreshUrl = rUrl, _oAuth2Scopes = rScopes } =
1079+ l { _oAath2RefreshUrl = swaggerMappend lUrl rUrl, _oAuth2Scopes = lScopes <> rScopes }
1080+
1081+-- swaggerMappend has First-like semantics, and here we need mappend'ing under Maybes.
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+
1094+instance Semigroup SecurityScheme where
1095+ SecurityScheme (SecuritySchemeOAuth2 lFlows) lDesc
1096+ <> SecurityScheme (SecuritySchemeOAuth2 rFlows) rDesc =
1097+ SecurityScheme (SecuritySchemeOAuth2 $ lFlows <> rFlows) (swaggerMappend lDesc rDesc)
1098+ l <> _ = l
1099+
1100+instance Semigroup SecurityDefinitions where
1101+ (SecurityDefinitions sd1) <> (SecurityDefinitions sd2) =
1102+ SecurityDefinitions $ InsOrdHashMap.unionWith (<>) sd1 sd2
1103+
1104+instance Monoid SecurityDefinitions where
1105+ mempty = SecurityDefinitions InsOrdHashMap.empty
1106+ mappend = (<>)
1107+
1108+instance Semigroup RequestBody where
1109+ (<>) = genericMappend
1110+instance Monoid RequestBody where
1111+ mempty = genericMempty
1112+ mappend = (<>)
1113+
1114+-- =======================================================================
1115+-- SwaggerMonoid helper instances
1116+-- =======================================================================
1117+
1118+instance SwaggerMonoid Info
1119+instance SwaggerMonoid Components
1120+instance SwaggerMonoid PathItem
1121+instance SwaggerMonoid Schema
1122+instance SwaggerMonoid Param
1123+instance SwaggerMonoid Responses
1124+instance SwaggerMonoid Response
1125+instance SwaggerMonoid ExternalDocs
1126+instance SwaggerMonoid Operation
1127+instance (Eq a, Hashable a) => SwaggerMonoid (InsOrdHashSet a)
1128+instance SwaggerMonoid SecurityDefinitions
1129+
1130+instance SwaggerMonoid MimeList
1131+deriving instance SwaggerMonoid URL
1132+
1133+instance SwaggerMonoid OpenApiType where
1134+ swaggerMempty = OpenApiString
1135+ swaggerMappend _ y = y
1136+
1137+instance SwaggerMonoid ParamLocation where
1138+ swaggerMempty = ParamQuery
1139+ swaggerMappend _ y = y
1140+
1141+instance {-# OVERLAPPING #-} SwaggerMonoid (InsOrdHashMap FilePath PathItem) where
1142+ swaggerMempty = InsOrdHashMap.empty
1143+ swaggerMappend = InsOrdHashMap.unionWith mappend
1144+
1145+instance Monoid a => SwaggerMonoid (Referenced a) where
1146+ swaggerMempty = Inline mempty
1147+ swaggerMappend (Inline x) (Inline y) = Inline (mappend x y)
1148+ swaggerMappend _ y = y
1149+
1150+-- =======================================================================
1151+-- Simple Generic-based ToJSON instances
1152+-- =======================================================================
1153+
1154+instance ToJSON Style where
1155+ toJSON = genericToJSON (jsonPrefix "Style")
1156+
1157+instance ToJSON OpenApiType where
1158+ toJSON = genericToJSON (jsonPrefix "Swagger")
1159+
1160+instance ToJSON ParamLocation where
1161+ toJSON = genericToJSON (jsonPrefix "Param")
1162+
1163+instance ToJSON Info where
1164+ toJSON = genericToJSON (jsonPrefix "Info")
1165+
1166+instance ToJSON Contact where
1167+ toJSON = genericToJSON (jsonPrefix "Contact")
1168+
1169+instance ToJSON License where
1170+ toJSON = genericToJSON (jsonPrefix "License")
1171+
1172+instance ToJSON ServerVariable where
1173+ toJSON = genericToJSON (jsonPrefix "ServerVariable")
1174+
1175+instance ToJSON ApiKeyLocation where
1176+ toJSON = genericToJSON (jsonPrefix "ApiKey")
1177+
1178+instance ToJSON ApiKeyParams where
1179+ toJSON = genericToJSON (jsonPrefix "apiKey")
1180+
1181+instance ToJSON Tag where
1182+ toJSON = genericToJSON (jsonPrefix "Tag")
1183+
1184+instance ToJSON ExternalDocs where
1185+ toJSON = genericToJSON (jsonPrefix "ExternalDocs")
1186+
1187+instance ToJSON Xml where
1188+ toJSON = genericToJSON (jsonPrefix "Xml")
1189+
1190+instance ToJSON Discriminator where
1191+ toJSON = genericToJSON (jsonPrefix "Discriminator")
1192+
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+
1205+-- =======================================================================
1206+-- Simple Generic-based FromJSON instances
1207+-- =======================================================================
1208+
1209+instance FromJSON Style where
1210+ parseJSON = genericParseJSON (jsonPrefix "Style")
1211+
1212+instance FromJSON OpenApiType where
1213+ parseJSON = genericParseJSON (jsonPrefix "Swagger")
1214+
1215+instance FromJSON ParamLocation where
1216+ parseJSON = genericParseJSON (jsonPrefix "Param")
1217+
1218+instance FromJSON Info where
1219+ parseJSON = genericParseJSON (jsonPrefix "Info")
1220+
1221+instance FromJSON Contact where
1222+ parseJSON = genericParseJSON (jsonPrefix "Contact")
1223+
1224+instance FromJSON License where
1225+ parseJSON = genericParseJSON (jsonPrefix "License")
1226+
1227+instance FromJSON ServerVariable where
1228+ parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
1229+
1230+instance FromJSON ApiKeyLocation where
1231+ parseJSON = genericParseJSON (jsonPrefix "ApiKey")
1232+
1233+instance FromJSON ApiKeyParams where
1234+ parseJSON = genericParseJSON (jsonPrefix "apiKey")
1235+
1236+instance FromJSON Tag where
1237+ parseJSON = genericParseJSON (jsonPrefix "Tag")
1238+
1239+instance FromJSON ExternalDocs where
1240+ parseJSON = genericParseJSON (jsonPrefix "ExternalDocs")
1241+
1242+instance FromJSON Discriminator where
1243+ parseJSON = genericParseJSON (jsonPrefix "Discriminator")
1244+
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+
1257+-- =======================================================================
1258+-- Manual ToJSON instances
1259+-- =======================================================================
1260+
1261+instance ToJSON MediaType where
1262+ toJSON = toJSON . show
1263+ toEncoding = toEncoding . show
1264+
1265+instance ToJSONKey MediaType where
1266+ toJSONKey = JSON.toJSONKeyText (Text.pack . show)
1267+
1268+instance (Eq p, ToJSON p, AesonDefaultValue p) => ToJSON (OAuth2Flow p) where
1269+ toJSON = sopSwaggerGenericToJSON
1270+ toEncoding = sopSwaggerGenericToEncoding
1271+
1272+instance ToJSON OAuth2Flows where
1273+ toJSON = sopSwaggerGenericToJSON
1274+ toEncoding = sopSwaggerGenericToEncoding
1275+
1276+instance 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+ ]
1290+ toJSON (SecuritySchemeApiKey params)
1291+ = toJSON params
1292+ <+> 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+ ]
1301+
1302+instance ToJSON OpenApi where
1303+ toJSON a = sopSwaggerGenericToJSON a &
1304+ if InsOrdHashMap.null (_openApiPaths a)
1305+ then (<+> object ["paths" .= object []])
1306+ else id
1307+ toEncoding = sopSwaggerGenericToEncoding
1308+
1309+instance ToJSON Server where
1310+ toJSON = sopSwaggerGenericToJSON
1311+ toEncoding = sopSwaggerGenericToEncoding
1312+
1313+instance ToJSON SecurityScheme where
1314+ toJSON = sopSwaggerGenericToJSON
1315+ toEncoding = sopSwaggerGenericToEncoding
1316+
1317+instance ToJSON Schema where
1318+ toJSON = sopSwaggerGenericToJSONWithOpts $
1319+ mkSwaggerAesonOptions "schema" & saoSubObject ?~ "items"
1320+
1321+instance ToJSON Header where
1322+ toJSON = sopSwaggerGenericToJSON
1323+ toEncoding = sopSwaggerGenericToEncoding
1324+
1325+-- | As for nullary schema for 0-arity type constructors, see
1326+-- <https://github.com/GetShopTV/swagger2/issues/167>.
1327+--
1328+-- >>> BSL.putStrLn $ encodePretty (OpenApiItemsArray [])
1329+-- {
1330+-- "example": [],
1331+-- "items": {},
1332+-- "maxItems": 0
1333+-- }
1334+--
1335+instance ToJSON OpenApiItems where
1336+ toJSON (OpenApiItemsObject x) = object [ "items" .= x ]
1337+ toJSON (OpenApiItemsArray []) = object
1338+ [ "items" .= object []
1339+ , "maxItems" .= (0 :: Int)
1340+ , "example" .= Array mempty
1341+ ]
1342+ toJSON (OpenApiItemsArray x) = object [ "items" .= x ]
1343+
1344+instance ToJSON Components where
1345+ toJSON = sopSwaggerGenericToJSON
1346+ toEncoding = sopSwaggerGenericToEncoding
1347+
1348+instance ToJSON MimeList where
1349+ toJSON (MimeList xs) = toJSON (map show xs)
1350+
1351+instance ToJSON Param where
1352+ toJSON = sopSwaggerGenericToJSON
1353+ toEncoding = sopSwaggerGenericToEncoding
1354+
1355+instance ToJSON Responses where
1356+ toJSON = sopSwaggerGenericToJSON
1357+ toEncoding = sopSwaggerGenericToEncoding
1358+
1359+instance ToJSON Response where
1360+ toJSON = sopSwaggerGenericToJSON
1361+ toEncoding = sopSwaggerGenericToEncoding
1362+
1363+instance ToJSON Operation where
1364+ toJSON = sopSwaggerGenericToJSON
1365+ toEncoding = sopSwaggerGenericToEncoding
1366+
1367+instance ToJSON PathItem where
1368+ toJSON = sopSwaggerGenericToJSON
1369+ toEncoding = sopSwaggerGenericToEncoding
1370+
1371+instance ToJSON RequestBody where
1372+ toJSON = sopSwaggerGenericToJSON
1373+ toEncoding = sopSwaggerGenericToEncoding
1374+
1375+instance ToJSON MediaTypeObject where
1376+ toJSON = sopSwaggerGenericToJSON
1377+ toEncoding = sopSwaggerGenericToEncoding
1378+
1379+instance ToJSON Example where
1380+ toJSON = sopSwaggerGenericToJSON
1381+ toEncoding = sopSwaggerGenericToEncoding
1382+
1383+instance ToJSON Encoding where
1384+ toJSON = sopSwaggerGenericToJSON
1385+ toEncoding = sopSwaggerGenericToEncoding
1386+
1387+instance ToJSON Link where
1388+ toJSON = sopSwaggerGenericToJSON
1389+ toEncoding = sopSwaggerGenericToEncoding
1390+
1391+instance ToJSON SecurityDefinitions where
1392+ toJSON (SecurityDefinitions sd) = toJSON sd
1393+
1394+instance ToJSON Reference where
1395+ toJSON (Reference ref) = object [ "$ref" .= ref ]
1396+
1397+referencedToJSON :: ToJSON a => Text -> Referenced a -> Value
1398+referencedToJSON prefix (Ref (Reference ref)) = object [ "$ref" .= (prefix <> ref) ]
1399+referencedToJSON _ (Inline x) = toJSON x
1400+
1401+instance ToJSON (Referenced Schema) where toJSON = referencedToJSON "#/components/schemas/"
1402+instance ToJSON (Referenced Param) where toJSON = referencedToJSON "#/components/parameters/"
1403+instance ToJSON (Referenced Response) where toJSON = referencedToJSON "#/components/responses/"
1404+instance ToJSON (Referenced RequestBody) where toJSON = referencedToJSON "#/components/requestBodies/"
1405+instance ToJSON (Referenced Example) where toJSON = referencedToJSON "#/components/examples/"
1406+instance 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/"
1409+
1410+instance ToJSON AdditionalProperties where
1411+ toJSON (AdditionalPropertiesAllowed b) = toJSON b
1412+ toJSON (AdditionalPropertiesSchema s) = toJSON s
1413+
1414+instance ToJSON ExpressionOrValue where
1415+ toJSON (Expression expr) = toJSON expr
1416+ toJSON (Value val) = toJSON val
1417+
1418+instance ToJSON Callback where
1419+ toJSON (Callback ps) = toJSON ps
1420+
1421+-- =======================================================================
1422+-- Manual FromJSON instances
1423+-- =======================================================================
1424+
1425+instance FromJSON MediaType where
1426+ parseJSON = withText "MediaType" $ \str ->
1427+ maybe (fail $ "Invalid media type literal " <> Text.unpack str) pure $ parseAccept $ encodeUtf8 str
1428+
1429+instance FromJSONKey MediaType where
1430+ fromJSONKey = FromJSONKeyTextParser (parseJSON . String)
1431+
1432+instance (Eq p, FromJSON p, AesonDefaultValue p) => FromJSON (OAuth2Flow p) where
1433+ parseJSON = sopSwaggerGenericParseJSON
1434+
1435+instance FromJSON OAuth2Flows where
1436+ parseJSON = sopSwaggerGenericParseJSON
1437+
1438+instance FromJSON SecuritySchemeType where
1439+ parseJSON js@(Object o) = do
1440+ (t :: Text) <- o .: "type"
1441+ 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
1448+ "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1449+ "oauth2" -> SecuritySchemeOAuth2 <$> (o .: "flows")
1450+ "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
1451+ _ -> empty
1452+ parseJSON _ = empty
1453+
1454+instance FromJSON OpenApi where
1455+ parseJSON = sopSwaggerGenericParseJSON
1456+
1457+instance FromJSON Server where
1458+ parseJSON = sopSwaggerGenericParseJSON
1459+
1460+instance FromJSON SecurityScheme where
1461+ parseJSON = sopSwaggerGenericParseJSON
1462+
1463+instance FromJSON Schema where
1464+ parseJSON = fmap nullaryCleanup . sopSwaggerGenericParseJSON
1465+ where nullaryCleanup :: Schema -> Schema
1466+ nullaryCleanup s =
1467+ if _schemaItems s == Just (OpenApiItemsArray [])
1468+ then s { _schemaExample = Nothing
1469+ , _schemaMaxItems = Nothing
1470+ }
1471+ else s
1472+
1473+instance FromJSON Header where
1474+ parseJSON = sopSwaggerGenericParseJSON
1475+
1476+instance FromJSON OpenApiItems where
1477+ parseJSON js@(Object obj)
1478+ | null obj = pure $ OpenApiItemsArray [] -- Nullary schema.
1479+ | otherwise = OpenApiItemsObject <$> parseJSON js
1480+ parseJSON js@(Array _) = OpenApiItemsArray <$> parseJSON js
1481+ parseJSON _ = empty
1482+
1483+instance FromJSON Components where
1484+ parseJSON = sopSwaggerGenericParseJSON
1485+
1486+instance FromJSON MimeList where
1487+ parseJSON js = MimeList . map fromString <$> parseJSON js
1488+
1489+instance FromJSON Param where
1490+ parseJSON = sopSwaggerGenericParseJSON
1491+
1492+instance FromJSON Responses where
1493+ parseJSON (Object o) = Responses
1494+ <$> o .:? "default"
1495+ <*> parseJSON (Object (deleteKey "default" o))
1496+ parseJSON _ = empty
1497+
1498+instance FromJSON Example where
1499+ parseJSON = sopSwaggerGenericParseJSON
1500+
1501+instance FromJSON Response where
1502+ parseJSON = sopSwaggerGenericParseJSON
1503+
1504+instance FromJSON Operation where
1505+ parseJSON = sopSwaggerGenericParseJSON
1506+
1507+instance FromJSON PathItem where
1508+ parseJSON = sopSwaggerGenericParseJSON
1509+
1510+instance FromJSON SecurityDefinitions where
1511+ parseJSON js = SecurityDefinitions <$> parseJSON js
1512+
1513+instance FromJSON RequestBody where
1514+ parseJSON = sopSwaggerGenericParseJSON
1515+
1516+instance FromJSON MediaTypeObject where
1517+ parseJSON = sopSwaggerGenericParseJSON
1518+
1519+instance FromJSON Encoding where
1520+ parseJSON = sopSwaggerGenericParseJSON
1521+
1522+instance FromJSON Link where
1523+ parseJSON = sopSwaggerGenericParseJSON
1524+
1525+instance FromJSON Reference where
1526+ parseJSON (Object o) = Reference <$> o .: "$ref"
1527+ parseJSON _ = empty
1528+
1529+referencedParseJSON :: FromJSON a => Text -> Value -> JSON.Parser (Referenced a)
1530+referencedParseJSON prefix js@(Object o) = do
1531+ ms <- o .:? "$ref"
1532+ case ms of
1533+ Nothing -> Inline <$> parseJSON js
1534+ Just s -> Ref <$> parseRef s
1535+ where
1536+ parseRef s = do
1537+ case Text.stripPrefix prefix s of
1538+ Nothing -> fail $ "expected $ref of the form \"" <> Text.unpack prefix <> "*\", but got " <> show s
1539+ Just suffix -> pure (Reference suffix)
1540+referencedParseJSON _ _ = fail "referenceParseJSON: not an object"
1541+
1542+instance FromJSON (Referenced Schema) where parseJSON = referencedParseJSON "#/components/schemas/"
1543+instance FromJSON (Referenced Param) where parseJSON = referencedParseJSON "#/components/parameters/"
1544+instance FromJSON (Referenced Response) where parseJSON = referencedParseJSON "#/components/responses/"
1545+instance FromJSON (Referenced RequestBody) where parseJSON = referencedParseJSON "#/components/requestBodies/"
1546+instance FromJSON (Referenced Example) where parseJSON = referencedParseJSON "#/components/examples/"
1547+instance 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/"
1550+
1551+instance FromJSON Xml where
1552+ parseJSON = genericParseJSON (jsonPrefix "xml")
1553+
1554+instance FromJSON AdditionalProperties where
1555+ parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
1556+ parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
1557+
1558+-- | All strings are parsed as expressions
1559+instance FromJSON ExpressionOrValue where
1560+ parseJSON (String expr) = pure $ Expression expr
1561+ parseJSON v = pure $ Value v
1562+
1563+instance FromJSON Callback where
1564+ parseJSON = fmap Callback . parseJSON
1565+
1566+instance HasSwaggerAesonOptions Server where
1567+ swaggerAesonOptions _ = mkSwaggerAesonOptions "server"
1568+instance HasSwaggerAesonOptions Components where
1569+ swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
1570+instance 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"
1576+instance HasSwaggerAesonOptions Operation where
1577+ swaggerAesonOptions _ = mkSwaggerAesonOptions "operation"
1578+instance HasSwaggerAesonOptions Param where
1579+ swaggerAesonOptions _ = mkSwaggerAesonOptions "param"
1580+instance HasSwaggerAesonOptions PathItem where
1581+ swaggerAesonOptions _ = mkSwaggerAesonOptions "pathItem"
1582+instance HasSwaggerAesonOptions Response where
1583+ swaggerAesonOptions _ = mkSwaggerAesonOptions "response"
1584+instance HasSwaggerAesonOptions RequestBody where
1585+ swaggerAesonOptions _ = mkSwaggerAesonOptions "requestBody"
1586+instance HasSwaggerAesonOptions MediaTypeObject where
1587+ swaggerAesonOptions _ = mkSwaggerAesonOptions "mediaTypeObject"
1588+instance HasSwaggerAesonOptions Responses where
1589+ swaggerAesonOptions _ = mkSwaggerAesonOptions "responses" & saoSubObject ?~ "responses"
1590+instance HasSwaggerAesonOptions SecurityScheme where
1591+ swaggerAesonOptions _ = mkSwaggerAesonOptions "securityScheme" & saoSubObject ?~ "type"
1592+instance HasSwaggerAesonOptions Schema where
1593+ swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1594+instance HasSwaggerAesonOptions OpenApi where
1595+ swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.0")]
1596+instance HasSwaggerAesonOptions Example where
1597+ swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
1598+instance HasSwaggerAesonOptions Encoding where
1599+ swaggerAesonOptions _ = mkSwaggerAesonOptions "encoding"
1600+
1601+instance HasSwaggerAesonOptions Link where
1602+ swaggerAesonOptions _ = mkSwaggerAesonOptions "link"
1603+
1604+instance AesonDefaultValue Server
1605+instance 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)
1611+instance AesonDefaultValue Responses
1612+instance AesonDefaultValue SecuritySchemeType
1613+instance AesonDefaultValue OpenApiType
1614+instance AesonDefaultValue MimeList where defaultValue = Just mempty
1615+instance AesonDefaultValue Info
1616+instance AesonDefaultValue ParamLocation
1617+instance AesonDefaultValue Link
1618+instance AesonDefaultValue SecurityDefinitions where
1619+ defaultValue = Just mempty
src/Data/Swagger/Internal/AesonUtils.hs → src/Data/OpenApi/Internal/AesonUtils.hsrenamed+17−36
@@ -1,20 +1,15 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DataKinds #-}
32{-# LANGUAGE FlexibleContexts #-}
43{-# LANGUAGE GADTs #-}
54{-# LANGUAGE ScopedTypeVariables #-}
65{-# LANGUAGE ExplicitForAll #-}
76{-# LANGUAGE TemplateHaskell #-}
8−#if __GLASGOW_HASKELL__ >= 800
97{-# LANGUAGE UndecidableSuperClasses #-}
10−#endif
11−module Data.Swagger.Internal.AesonUtils (
8+module Data.OpenApi.Internal.AesonUtils (
129 -- * Generic functions
1310 AesonDefaultValue(..),
1411 sopSwaggerGenericToJSON,
15−#if MIN_VERSION_aeson(0,10,0)
1612 sopSwaggerGenericToEncoding,
17−#endif
1813 sopSwaggerGenericToJSONWithOpts,
1914 sopSwaggerGenericParseJSON,
2015 -- * Options
@@ -32,7 +27,7 @@ import Prelude.Compat
3227import Control.Applicative ((<|>))
3328import Control.Lens (makeLenses, (^.))
3429import Control.Monad (unless)
35−import Data.Aeson (ToJSON(..), FromJSON(..), Value(..), Object, object, (.:), (.:?), (.!=), withObject)
30+import Data.Aeson (ToJSON(..), FromJSON(..), Value(..), Object, object, (.:), (.:?), (.!=), withObject, Encoding, pairs, (.=), Series)
3631import Data.Aeson.Types (Parser, Pair)
3732import Data.Char (toLower, isUpper)
3833import Data.Foldable (traverse_)
@@ -41,13 +36,11 @@ import Data.Text (Text)
4136import Generics.SOP
4237
4338import qualified Data.Text as T
44−import qualified Data.HashMap.Strict as HM
4539import qualified Data.Set as Set
4640import qualified Data.HashMap.Strict.InsOrd as InsOrd
41+import qualified Data.HashSet.InsOrd as InsOrdHS
4742
48−#if MIN_VERSION_aeson(0,10,0)
49−import Data.Aeson (Encoding, pairs, (.=), Series)
50−#endif
43+import Data.OpenApi.Aeson.Compat (keyToString, objectToList, stringToKey)
5144
5245-------------------------------------------------------------------------------
5346-- SwaggerAesonOptions
@@ -55,7 +48,7 @@ import Data.Aeson (Encoding, pairs, (.=), Series)
5548
5649data SwaggerAesonOptions = SwaggerAesonOptions
5750 { _saoPrefix :: String
58− , _saoAdditionalPairs :: [(Text, Value)]
51+ , _saoAdditionalPairs :: [Pair]
5952 , _saoSubObject :: Maybe String
6053 }
6154
@@ -85,6 +78,7 @@ instance AesonDefaultValue Text where defaultValue = Nothing
8578instance AesonDefaultValue (Maybe a) where defaultValue = Just Nothing
8679instance AesonDefaultValue [a] where defaultValue = Just []
8780instance AesonDefaultValue (Set.Set a) where defaultValue = Just Set.empty
81+instance AesonDefaultValue (InsOrdHS.InsOrdHashSet k) where defaultValue = Just InsOrdHS.empty
8882instance AesonDefaultValue (InsOrd.InsOrdHashMap k v) where defaultValue = Just InsOrd.empty
8983
9084-------------------------------------------------------------------------------
@@ -144,7 +138,7 @@ sopSwaggerGenericToJSON'
144138 -> DatatypeInfo '[xs]
145139 -> POP Maybe '[xs]
146140 -> [Pair]
147−sopSwaggerGenericToJSON' opts (SOP (Z fields)) (ADT _ _ (Record _ fieldsInfo :* Nil)) (POP (defs :* Nil)) =
141+sopSwaggerGenericToJSON' opts (SOP (Z fields)) (ADT _ _ (Record _ fieldsInfo :* Nil) _) (POP (defs :* Nil)) =
148142 sopSwaggerGenericToJSON'' opts fields fieldsInfo defs
149143sopSwaggerGenericToJSON' _ _ _ _ = error "sopSwaggerGenericToJSON: unsupported type"
150144
@@ -161,21 +155,18 @@ sopSwaggerGenericToJSON'' (SwaggerAesonOptions prefix _ sub) = go
161155 go Nil Nil Nil = []
162156 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
163157 | Just name' == sub = case json of
164− Object m -> HM.toList m ++ rest
158+ Object m -> objectToList m ++ rest
165159 Null -> rest
166160 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show json
167161 -- If default value: omit it.
168162 | Just x == def =
169163 rest
170164 | otherwise =
171− (T.pack name', json) : rest
165+ (stringToKey name', json) : rest
172166 where
173167 json = toJSON x
174168 name' = fieldNameModifier name
175169 rest = go xs names defs
176−#if __GLASGOW_HASKELL__ < 800
177− go _ _ _ = error "not empty"
178−#endif
179170
180171 fieldNameModifier = modifier . drop 1
181172 modifier = lowerFirstUppers . drop (length prefix)
@@ -205,11 +196,11 @@ sopSwaggerGenericParseJSON = withObject "Swagger Record Object" $ \obj ->
205196 proxy = Proxy :: Proxy a
206197 opts = swaggerAesonOptions proxy
207198
208− parseAdditionalField :: Object -> (Text, Value) -> Parser ()
199+ parseAdditionalField :: Object -> Pair -> Parser ()
209200 parseAdditionalField obj (k, v) = do
210201 v' <- obj .: k
211202 unless (v == v') $ fail $
212− "Additonal field don't match for key " ++ T.unpack k
203+ "Additonal field don't match for key " ++ keyToString k
213204 ++ ": " ++ show v
214205 ++ " /= " ++ show v'
215206
@@ -220,7 +211,7 @@ sopSwaggerGenericParseJSON'
220211 -> DatatypeInfo '[xs]
221212 -> POP Maybe '[xs]
222213 -> Parser (SOP I '[xs])
223−sopSwaggerGenericParseJSON' opts obj (ADT _ _ (Record _ fieldsInfo :* Nil)) (POP (defs :* Nil)) =
214+sopSwaggerGenericParseJSON' opts obj (ADT _ _ (Record _ fieldsInfo :* Nil) _) (POP (defs :* Nil)) =
224215 SOP . Z <$> sopSwaggerGenericParseJSON'' opts obj fieldsInfo defs
225216sopSwaggerGenericParseJSON' _ _ _ _ = error "sopSwaggerGenericParseJSON: unsupported type"
226217
@@ -240,8 +231,8 @@ sopSwaggerGenericParseJSON'' (SwaggerAesonOptions prefix _ sub) obj = go
240231 -- Note: we might strip fields of outer structure.
241232 cons <$> (withDef $ parseJSON $ Object obj) <*> rest
242233 | otherwise = case def of
243− Just def' -> cons <$> obj .:? T.pack name' .!= def' <*> rest
244− Nothing -> cons <$> obj .: T.pack name' <*> rest
234+ Just def' -> cons <$> obj .:? stringToKey name' .!= def' <*> rest
235+ Nothing -> cons <$> obj .: stringToKey name' <*> rest
245236 where
246237 cons h t = I h :* t
247238 name' = fieldNameModifier name
@@ -250,9 +241,6 @@ sopSwaggerGenericParseJSON'' (SwaggerAesonOptions prefix _ sub) obj = go
250241 withDef = case def of
251242 Just def' -> (<|> pure def')
252243 Nothing -> id
253−#if __GLASGOW_HASKELL__ < 800
254− go _ _ = error "not empty"
255−#endif
256244
257245 fieldNameModifier = modifier . drop 1
258246 modifier = lowerFirstUppers . drop (length prefix)
@@ -263,8 +251,6 @@ sopSwaggerGenericParseJSON'' (SwaggerAesonOptions prefix _ sub) obj = go
263251-- ToEncoding
264252-------------------------------------------------------------------------------
265253
266−#if MIN_VERSION_aeson(0,10,0)
267−
268254sopSwaggerGenericToEncoding
269255 :: forall a xs.
270256 ( HasDatatypeInfo a
@@ -292,7 +278,7 @@ sopSwaggerGenericToEncoding'
292278 -> DatatypeInfo '[xs]
293279 -> POP Maybe '[xs]
294280 -> Series
295−sopSwaggerGenericToEncoding' opts (SOP (Z fields)) (ADT _ _ (Record _ fieldsInfo :* Nil)) (POP (defs :* Nil)) =
281+sopSwaggerGenericToEncoding' opts (SOP (Z fields)) (ADT _ _ (Record _ fieldsInfo :* Nil) _) (POP (defs :* Nil)) =
296282 sopSwaggerGenericToEncoding'' opts fields fieldsInfo defs
297283sopSwaggerGenericToEncoding' _ _ _ _ = error "sopSwaggerGenericToEncoding: unsupported type"
298284
@@ -309,24 +295,19 @@ sopSwaggerGenericToEncoding'' (SwaggerAesonOptions prefix _ sub) = go
309295 go Nil Nil Nil = mempty
310296 go (I x :* xs) (FieldInfo name :* names) (def :* defs)
311297 | Just name' == sub = case toJSON x of
312− Object m -> pairsToSeries (HM.toList m) <> rest
298+ Object m -> pairsToSeries (objectToList m) <> rest
313299 Null -> rest
314300 _ -> error $ "sopSwaggerGenericToJSON: subjson is not an object: " ++ show (toJSON x)
315301 -- If default value: omit it.
316302 | Just x == def =
317303 rest
318304 | otherwise =
319− (T.pack name' .= x) <> rest
305+ (stringToKey name' .= x) <> rest
320306 where
321307 name' = fieldNameModifier name
322308 rest = go xs names defs
323−#if __GLASGOW_HASKELL__ < 800
324− go _ _ _ = error "not empty"
325−#endif
326309
327310 fieldNameModifier = modifier . drop 1
328311 modifier = lowerFirstUppers . drop (length prefix)
329312 lowerFirstUppers s = map toLower x ++ y
330313 where (x, y) = span isUpper s
331−
332−#endif
src/Data/Swagger/Internal/ParamSchema.hs → src/Data/OpenApi/Internal/ParamSchema.hsrenamed+90−67
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DataKinds #-}
32{-# LANGUAGE DefaultSignatures #-}
43{-# LANGUAGE FlexibleContexts #-}
@@ -12,14 +11,11 @@
1211{-# LANGUAGE TypeOperators #-}
1312{-# LANGUAGE TypeSynonymInstances #-}
1413{-# LANGUAGE UndecidableInstances #-}
15−#if __GLASGOW_HASKELL__ >= 800
1614-- Generic a is redundant in ToParamSchema a default imple
1715{-# OPTIONS_GHC -Wno-redundant-constraints #-}
1816-- For TypeErrors
1917{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
20−#endif
21−#include "overlapping-compat.h"
22−module Data.Swagger.Internal.ParamSchema where
18+module Data.OpenApi.Internal.ParamSchema where
2319
2420import Control.Lens
2521import Data.Aeson (ToJSON (..))
@@ -45,37 +41,40 @@ import Data.Word
4541import Data.UUID.Types (UUID)
4642import Web.Cookie (SetCookie)
4743
48−import Data.Swagger.Internal
49−import Data.Swagger.Lens
50−import Data.Swagger.SchemaOptions
44+import Data.OpenApi.Internal
45+import Data.OpenApi.Lens
46+import Data.OpenApi.SchemaOptions
5147
52−#if __GLASGOW_HASKELL__ < 800
53−#else
5448import qualified Data.ByteString as BS
55−import qualified Data.ByteString.Lazy as BSL
49+import qualified Data.ByteString.Lazy.Char8 as BSL
5650import GHC.TypeLits (TypeError, ErrorMessage(..))
57−#endif
5851
5952-- | Default schema for binary data (any sequence of octets).
60−binaryParamSchema :: ParamSchema t
61−binaryParamSchema = mempty
62− & type_ .~ SwaggerString
53+binarySchema :: Schema
54+binarySchema = mempty
55+ & type_ ?~ OpenApiString
6356 & format ?~ "binary"
6457
6558-- | Default schema for binary data (base64 encoded).
66−byteParamSchema :: ParamSchema t
67−byteParamSchema = mempty
68− & type_ .~ SwaggerString
59+byteSchema :: Schema
60+byteSchema = mempty
61+ & type_ ?~ OpenApiString
6962 & format ?~ "byte"
7063
7164-- | Default schema for password string.
7265-- @"password"@ format is used to hint UIs the input needs to be obscured.
73−passwordParamSchema :: ParamSchema t
74−passwordParamSchema = mempty
75− & type_ .~ SwaggerString
66+passwordSchema :: Schema
67+passwordSchema = mempty
68+ & type_ ?~ OpenApiString
7669 & format ?~ "password"
7770
78−-- | Convert a type into a plain @'ParamSchema'@.
71+-- | Convert a type into a plain @'Schema'@.
72+--
73+-- In previous versions of the package there was a separate type called @ParamSchema@, which was
74+-- included in a greater 'Schema'. Now this is a single class, but distinction for schema generators
75+-- for "simple" types is preserved.
76+--
77+-- 'ToParamSchema' is suited only for primitive-like types without nested fields and such.
7978--
8079-- An example type and instance:
8180--
@@ -88,7 +87,7 @@ passwordParamSchema = mempty
8887--
8988-- instance ToParamSchema Direction where
9089-- toParamSchema _ = mempty
91−-- & type_ .~ SwaggerString
90+-- & type_ ?~ OpenApiString
9291-- & enum_ ?~ [ \"Up\", \"Down\" ]
9392-- @
9493--
@@ -113,24 +112,26 @@ passwordParamSchema = mempty
113112class ToParamSchema a where
114113 -- | Convert a type into a plain parameter schema.
115114 --
116− -- >>> encode $ toParamSchema (Proxy :: Proxy Integer)
117− -- "{\"type\":\"integer\"}"
118− toParamSchema :: Proxy a -> ParamSchema t
119− default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> ParamSchema t
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
120121 toParamSchema = genericToParamSchema defaultSchemaOptions
121122
122−instance OVERLAPPING_ ToParamSchema String where
123− toParamSchema _ = mempty & type_ .~ SwaggerString
123+instance {-# OVERLAPPING #-} ToParamSchema String where
124+ toParamSchema _ = mempty & type_ ?~ OpenApiString
124125
125126instance ToParamSchema Bool where
126− toParamSchema _ = mempty & type_ .~ SwaggerBoolean
127+ toParamSchema _ = mempty & type_ ?~ OpenApiBoolean
127128
128129instance ToParamSchema Integer where
129− toParamSchema _ = mempty & type_ .~ SwaggerInteger
130+ toParamSchema _ = mempty & type_ ?~ OpenApiInteger
130131
131132instance ToParamSchema Natural where
132133 toParamSchema _ = mempty
133− & type_ .~ SwaggerInteger
134+ & type_ ?~ OpenApiInteger
134135 & minimum_ ?~ 0
135136 & exclusiveMinimum ?~ False
136137
@@ -147,52 +148,66 @@ instance ToParamSchema Int64 where
147148instance ToParamSchema Word where toParamSchema = toParamSchemaBoundedIntegral
148149instance ToParamSchema Word8 where toParamSchema = toParamSchemaBoundedIntegral
149150instance ToParamSchema Word16 where toParamSchema = toParamSchemaBoundedIntegral
150−instance ToParamSchema Word32 where toParamSchema = toParamSchemaBoundedIntegral
151−instance ToParamSchema Word64 where toParamSchema = toParamSchemaBoundedIntegral
151+
152+instance ToParamSchema Word32 where
153+ toParamSchema proxy = toParamSchemaBoundedIntegral proxy & format ?~ "int32"
154+
155+instance ToParamSchema Word64 where
156+ toParamSchema proxy = toParamSchemaBoundedIntegral proxy & format ?~ "int64"
152157
153158-- | Default plain schema for @'Bounded'@, @'Integral'@ types.
154159--
155−-- >>> encode $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
156−-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
157−toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> ParamSchema t
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
158167toParamSchemaBoundedIntegral _ = mempty
159− & type_ .~ SwaggerInteger
168+ & type_ ?~ OpenApiInteger
160169 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
161170 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
162171
163172instance ToParamSchema Char where
164173 toParamSchema _ = mempty
165− & type_ .~ SwaggerString
174+ & type_ ?~ OpenApiString
166175 & maxLength ?~ 1
167176 & minLength ?~ 1
168177
169178instance ToParamSchema Scientific where
170− toParamSchema _ = mempty & type_ .~ SwaggerNumber
179+ toParamSchema _ = mempty & type_ ?~ OpenApiNumber
171180
172181instance HasResolution a => ToParamSchema (Fixed a) where
173182 toParamSchema _ = mempty
174− & type_ .~ SwaggerNumber
183+ & type_ ?~ OpenApiNumber
175184 & multipleOf ?~ (recip . fromInteger $ resolution (Proxy :: Proxy a))
176185
177186instance ToParamSchema Double where
178187 toParamSchema _ = mempty
179− & type_ .~ SwaggerNumber
188+ & type_ ?~ OpenApiNumber
180189 & format ?~ "double"
181190
182191instance ToParamSchema Float where
183192 toParamSchema _ = mempty
184− & type_ .~ SwaggerNumber
193+ & type_ ?~ OpenApiNumber
185194 & format ?~ "float"
186195
187−timeParamSchema :: String -> ParamSchema t
196+timeParamSchema :: String -> Schema
188197timeParamSchema fmt = mempty
189− & type_ .~ SwaggerString
198+ & type_ ?~ OpenApiString
190199 & format ?~ T.pack fmt
191200
192201-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
193202instance ToParamSchema Day where
194203 toParamSchema _ = timeParamSchema "date"
195204
205+-- |
206+-- >>> toParamSchema (Proxy :: Proxy TimeOfDay) ^. format
207+-- Just "hh:MM:ss"
208+instance ToParamSchema TimeOfDay where
209+ toParamSchema _ = timeParamSchema "hh:MM:ss"
210+
196211-- |
197212-- >>> toParamSchema (Proxy :: Proxy LocalTime) ^. format
198213-- Just "yyyy-mm-ddThh:MM:ss"
@@ -201,9 +216,9 @@ instance ToParamSchema LocalTime where
201216
202217-- |
203218-- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. format
204−-- Just "yyyy-mm-ddThh:MM:ss+hhMM"
219+-- Just "date-time"
205220instance ToParamSchema ZonedTime where
206− toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss+hhMM"
221+ toParamSchema _ = timeParamSchema "date-time"
207222
208223-- |
209224-- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. format
@@ -222,25 +237,21 @@ instance ToParamSchema TL.Text where
222237
223238instance ToParamSchema Version where
224239 toParamSchema _ = mempty
225− & type_ .~ SwaggerString
240+ & type_ ?~ OpenApiString
226241 & pattern ?~ "^\\d+(\\.\\d+)*$"
227242
228243instance ToParamSchema SetCookie where
229244 toParamSchema _ = mempty
230− & type_ .~ SwaggerString
231−
245+ & type_ ?~ OpenApiString
232246
233−#if __GLASGOW_HASKELL__ < 800
234−#else
235247type family ToParamSchemaByteStringError bs where
236248 ToParamSchemaByteStringError bs = TypeError
237249 ( 'Text "Impossible to have an instance " :<>: ShowType (ToParamSchema bs) :<>: Text "."
238250 :$$: 'Text "Please, use a newtype wrapper around " :<>: ShowType bs :<>: Text " instead."
239− :$$: 'Text "Consider using byteParamSchema or binaryParamSchema templates." )
251+ :$$: 'Text "Consider using byteParamSchema or binaryParamSchemaemplates." )
240252
241253instance ToParamSchemaByteStringError BS.ByteString => ToParamSchema BS.ByteString where toParamSchema = error "impossible"
242254instance ToParamSchemaByteStringError BSL.ByteString => ToParamSchema BSL.ByteString where toParamSchema = error "impossible"
243−#endif
244255
245256instance ToParamSchema All where toParamSchema _ = toParamSchema (Proxy :: Proxy Bool)
246257instance ToParamSchema Any where toParamSchema _ = toParamSchema (Proxy :: Proxy Bool)
@@ -254,8 +265,8 @@ instance ToParamSchema a => ToParamSchema (Identity a) where toParamSchema _ = t
254265
255266instance ToParamSchema a => ToParamSchema [a] where
256267 toParamSchema _ = mempty
257− & type_ .~ SwaggerArray
258− & items ?~ SwaggerItemsPrimitive Nothing (toParamSchema (Proxy :: Proxy a))
268+ & type_ ?~ OpenApiArray
269+ & items ?~ OpenApiItemsObject (Inline $ toParamSchema (Proxy :: Proxy a))
259270
260271instance ToParamSchema a => ToParamSchema (V.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
261272instance ToParamSchema a => ToParamSchema (VP.Vector a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a])
@@ -270,29 +281,40 @@ instance ToParamSchema a => ToParamSchema (HashSet a) where
270281 toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a))
271282
272283-- |
273−-- >>> encode $ toParamSchema (Proxy :: Proxy ())
274−-- "{\"type\":\"string\",\"enum\":[\"_\"]}"
284+-- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy ())
285+-- {
286+-- "enum": [
287+-- "_"
288+-- ],
289+-- "type": "string"
290+-- }
275291instance ToParamSchema () where
276292 toParamSchema _ = mempty
277− & type_ .~ SwaggerString
293+ & type_ ?~ OpenApiString
278294 & enum_ ?~ ["_"]
279295
280296instance ToParamSchema UUID where
281297 toParamSchema _ = mempty
282− & type_ .~ SwaggerString
298+ & type_ ?~ OpenApiString
283299 & format ?~ "uuid"
284300
285−-- | A configurable generic @'ParamSchema'@ creator.
301+-- | A configurable generic @'Schema'@ creator.
286302--
287303-- >>> :set -XDeriveGeneric
288304-- >>> data Color = Red | Blue deriving Generic
289−-- >>> encode $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
290−-- "{\"type\":\"string\",\"enum\":[\"Red\",\"Blue\"]}"
291−genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> ParamSchema t
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
292314genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty
293315
294316class GToParamSchema (f :: * -> *) where
295− gtoParamSchema :: SchemaOptions -> Proxy f -> ParamSchema t -> ParamSchema t
317+ gtoParamSchema :: SchemaOptions -> Proxy f -> Schema -> Schema
296318
297319instance GToParamSchema f => GToParamSchema (D1 d f) where
298320 gtoParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy f)
@@ -310,14 +332,14 @@ instance (GEnumParamSchema f, GEnumParamSchema g) => GToParamSchema (f :+: g) wh
310332 gtoParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy (f :+: g))
311333
312334class GEnumParamSchema (f :: * -> *) where
313− genumParamSchema :: SchemaOptions -> Proxy f -> ParamSchema t -> ParamSchema t
335+ genumParamSchema :: SchemaOptions -> Proxy f -> Schema -> Schema
314336
315337instance (GEnumParamSchema f, GEnumParamSchema g) => GEnumParamSchema (f :+: g) where
316338 genumParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy f) . genumParamSchema opts (Proxy :: Proxy g)
317339
318340instance Constructor c => GEnumParamSchema (C1 c U1) where
319341 genumParamSchema opts _ s = s
320− & type_ .~ SwaggerString
342+ & type_ ?~ OpenApiString
321343 & enum_ %~ addEnumValue tag
322344 where
323345 tag = toJSON (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
@@ -329,3 +351,4 @@ data Proxy3 a b c = Proxy3
329351
330352-- $setup
331353-- >>> import Data.Aeson (encode)
354+-- >>> import Data.OpenApi.Internal.Utils
src/Data/Swagger/Internal/Schema.hs → src/Data/OpenApi/Internal/Schema.hsrenamed+459−211
@@ -1,38 +1,36 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DataKinds #-}
34{-# LANGUAGE DefaultSignatures #-}
45{-# LANGUAGE FlexibleContexts #-}
56{-# LANGUAGE FlexibleInstances #-}
67{-# LANGUAGE KindSignatures #-}
7−{-# LANGUAGE DataKinds #-}
88{-# LANGUAGE OverloadedLists #-}
99{-# LANGUAGE OverloadedStrings #-}
1010{-# LANGUAGE PackageImports #-}
1111{-# LANGUAGE PolyKinds #-}
1212{-# LANGUAGE ScopedTypeVariables #-}
1313{-# LANGUAGE TupleSections #-}
14+{-# LANGUAGE TypeApplications #-}
1415{-# LANGUAGE TypeFamilies #-}
1516{-# LANGUAGE TypeOperators #-}
1617{-# LANGUAGE TypeSynonymInstances #-}
1718{-# LANGUAGE UndecidableInstances #-}
18−#if __GLASGOW_HASKELL__ >= 800
19−-- Few generics related redundant constraints
2019{-# OPTIONS_GHC -Wno-redundant-constraints #-}
2120-- For TypeErrors
2221{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
23−#endif
24−#include "overlapping-compat.h"
25−module Data.Swagger.Internal.Schema where
22+module Data.OpenApi.Internal.Schema where
2623
2724import Prelude ()
2825import Prelude.Compat
2926
30−import Control.Lens
27+import Control.Lens hiding (allOf)
3128import Data.Data.Lens (template)
3229
3330import Control.Monad
3431import Control.Monad.Writer
35−import Data.Aeson (ToJSON (..), ToJSONKey (..), ToJSONKeyFunction (..), Value (..), Object(..))
32+import Data.Aeson (Object (..), SumEncoding (..), ToJSON (..), ToJSONKey (..),
33+ ToJSONKeyFunction (..), Value (..))
3634import Data.Char
3735import Data.Data (Data)
3836import Data.Foldable (traverse_)
@@ -44,8 +42,10 @@ import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
4442import Data.Int
4543import Data.IntSet (IntSet)
4644import Data.IntMap (IntMap)
45+import Data.List (sort)
4746import Data.List.NonEmpty.Compat (NonEmpty)
4847import Data.Map (Map)
48+import Data.Maybe (fromMaybe)
4949import Data.Proxy
5050import Data.Scientific (Scientific)
5151import Data.Fixed (Fixed, HasResolution, Pico)
@@ -62,21 +62,20 @@ import Numeric.Natural.Compat (Natural)
6262import Data.Word
6363import GHC.Generics
6464import qualified Data.UUID.Types as UUID
65+import Type.Reflection (Typeable, typeRep)
6566
66−import Data.Swagger.Declare
67−import Data.Swagger.Internal
68−import Data.Swagger.Internal.ParamSchema (ToParamSchema(..))
69−import Data.Swagger.Lens hiding (name, schema)
70−import qualified Data.Swagger.Lens as Swagger
71−import Data.Swagger.SchemaOptions
72−import Data.Swagger.Internal.TypeShape
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
7375
74−#if __GLASGOW_HASKELL__ < 800
75−#else
7676import qualified Data.ByteString as BS
77−import qualified Data.ByteString.Lazy as BSL
77+import qualified Data.ByteString.Lazy.Char8 as BSL
7878import GHC.TypeLits (TypeError, ErrorMessage(..))
79−#endif
8079
8180unnamed :: Schema -> NamedSchema
8281unnamed schema = NamedSchema Nothing schema
@@ -103,7 +102,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
103102--
104103-- import Control.Lens
105104-- import Data.Proxy
106−-- import Data.Swagger
105+-- import Data.OpenApi
107106--
108107-- data Coord = Coord { x :: Double, y :: Double }
109108--
@@ -111,7 +110,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
111110-- declareNamedSchema _ = do
112111-- doubleSchema <- declareSchemaRef (Proxy :: Proxy Double)
113112-- return $ NamedSchema (Just \"Coord\") $ mempty
114−-- & type_ .~ SwaggerObject
113+-- & type_ ?~ OpenApiObject
115114-- & properties .~
116115-- [ (\"x\", doubleSchema)
117116-- , (\"y\", doubleSchema)
@@ -137,16 +136,20 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
137136--
138137-- instance ToSchema Coord
139138-- @
140−class ToSchema a where
139+class Typeable a => ToSchema a where
141140 -- | Convert a type into an optionally named schema
142141 -- together with all used definitions.
143142 -- Note that the schema itself is included in definitions
144143 -- only if it is recursive (and thus needs its definition in scope).
145144 declareNamedSchema :: Proxy a -> Declare (Definitions Schema) NamedSchema
146− default declareNamedSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted") =>
145+ default declareNamedSchema :: (Generic a, GToSchema (Rep a)) =>
147146 Proxy a -> Declare (Definitions Schema) NamedSchema
148147 declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
149148
149+instance ToSchema TimeOfDay where
150+ declareNamedSchema _ = pure $ named "TimeOfDay" $ timeSchema "hh:MM:ss"
151+ & example ?~ toJSON (TimeOfDay 12 33 15)
152+
150153-- | Convert a type into a schema and declare all used schema definitions.
151154declareSchema :: ToSchema a => Proxy a -> Declare (Definitions Schema) Schema
152155declareSchema = fmap _namedSchemaSchema . declareNamedSchema
@@ -155,13 +158,19 @@ declareSchema = fmap _namedSchemaSchema . declareNamedSchema
155158--
156159-- >>> toNamedSchema (Proxy :: Proxy String) ^. name
157160-- Nothing
158−-- >>> encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
159−-- "{\"type\":\"string\"}"
161+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy String) ^. schema)
162+-- {
163+-- "type": "string"
164+-- }
160165--
161166-- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
162167-- Just "Day"
163−-- >>> encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
164−-- "{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}"
168+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy Day) ^. schema)
169+-- {
170+-- "example": "2016-07-22",
171+-- "format": "date",
172+-- "type": "string"
173+-- }
165174toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
166175toNamedSchema = undeclare . declareNamedSchema
167176
@@ -177,22 +186,35 @@ schemaName = _namedSchemaName . toNamedSchema
177186
178187-- | Convert a type into a schema.
179188--
180−-- >>> encode $ toSchema (Proxy :: Proxy Int8)
181−-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
189+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Int8)
190+-- {
191+-- "maximum": 127,
192+-- "minimum": -128,
193+-- "type": "integer"
194+-- }
182195--
183−-- >>> encode $ toSchema (Proxy :: Proxy [Day])
184−-- "{\"items\":{\"$ref\":\"#/definitions/Day\"},\"type\":\"array\"}"
196+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy [Day])
197+-- {
198+-- "items": {
199+-- "$ref": "#/components/schemas/Day"
200+-- },
201+-- "type": "array"
202+-- }
185203toSchema :: ToSchema a => Proxy a -> Schema
186204toSchema = _namedSchemaSchema . toNamedSchema
187205
188206-- | Convert a type into a referenced schema if possible.
189207-- Only named schemas can be referenced, nameless schemas are inlined.
190208--
191−-- >>> encode $ toSchemaRef (Proxy :: Proxy Integer)
192−-- "{\"type\":\"integer\"}"
209+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Integer)
210+-- {
211+-- "type": "integer"
212+-- }
193213--
194−-- >>> encode $ toSchemaRef (Proxy :: Proxy Day)
195−-- "{\"$ref\":\"#/definitions/Day\"}"
214+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Day)
215+-- {
216+-- "$ref": "#/components/schemas/Day"
217+-- }
196218toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
197219toSchemaRef = undeclare . declareSchemaRef
198220
@@ -260,8 +282,15 @@ inlineAllSchemas = inlineSchemasWhen (const True)
260282
261283-- | Convert a type into a schema without references.
262284--
263−-- >>> encode $ toInlinedSchema (Proxy :: Proxy [Day])
264−-- "{\"items\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"},\"type\":\"array\"}"
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+-- }
265294--
266295-- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
267296-- when inlining recursive schemas.
@@ -291,41 +320,67 @@ inlineNonRecursiveSchemas defs = inlineSchemasWhen nonRecursive defs
291320 traverse_ usedNames (InsOrdHashMap.lookup name defs)
292321 Inline subschema -> usedNames subschema
293322
294−-- | Default schema for binary data (any sequence of octets).
295−binarySchema :: Schema
296−binarySchema = mempty
297− & type_ .~ SwaggerString
298− & format ?~ "binary"
299−
300−-- | Default schema for binary data (base64 encoded).
301−byteSchema :: Schema
302−byteSchema = mempty
303− & type_ .~ SwaggerString
304− & format ?~ "byte"
305−
306−-- | Default schema for password string.
307−-- @"password"@ format is used to hint UIs the input needs to be obscured.
308−passwordSchema :: Schema
309−passwordSchema = mempty
310− & type_ .~ SwaggerString
311− & format ?~ "password"
312−
313323-- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
314324-- Produced schema can be used for further refinement.
315325--
316−-- >>> encode $ sketchSchema "hello"
317−-- "{\"example\":\"hello\",\"type\":\"string\"}"
326+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema "hello"
327+-- {
328+-- "example": "hello",
329+-- "type": "string"
330+-- }
318331--
319−-- >>> encode $ sketchSchema (1, 2, 3)
320−-- "{\"example\":[1,2,3],\"items\":{\"type\":\"number\"},\"type\":\"array\"}"
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+-- }
321344--
322−-- >>> encode $ sketchSchema ("Jack", 25)
323−-- "{\"example\":[\"Jack\",25],\"items\":[{\"type\":\"string\"},{\"type\":\"number\"}],\"type\":\"array\"}"
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+-- }
324361--
325362-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
326363-- >>> instance ToJSON Person
327−-- >>> encode $ sketchSchema (Person "Jack" 25)
328−-- "{\"required\":[\"age\",\"name\"],\"properties\":{\"age\":{\"type\":\"number\"},\"name\":{\"type\":\"string\"}},\"example\":{\"age\":25,\"name\":\"Jack\"},\"type\":\"object\"}"
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+-- }
329384sketchSchema :: ToJSON a => a -> Schema
330385sketchSchema = sketch . toJSON
331386 where
@@ -333,15 +388,15 @@ sketchSchema = sketch . toJSON
333388 sketch js@(Bool _) = go js
334389 sketch js = go js & example ?~ js
335390
336− go Null = mempty & type_ .~ SwaggerNull
337− go (Bool _) = mempty & type_ .~ SwaggerBoolean
338− go (String _) = mempty & type_ .~ SwaggerString
339− go (Number _) = mempty & type_ .~ SwaggerNumber
391+ go Null = mempty & type_ ?~ OpenApiNull
392+ go (Bool _) = mempty & type_ ?~ OpenApiBoolean
393+ go (String _) = mempty & type_ ?~ OpenApiString
394+ go (Number _) = mempty & type_ ?~ OpenApiNumber
340395 go (Array xs) = mempty
341− & type_ .~ SwaggerArray
396+ & type_ ?~ OpenApiArray
342397 & items ?~ case ischema of
343− Just s -> SwaggerItemsObject (Inline s)
344− _ -> SwaggerItemsArray (map Inline ys)
398+ Just s -> OpenApiItemsObject (Inline s)
399+ _ -> OpenApiItemsArray (map Inline ys)
345400 where
346401 ys = map go (V.toList xs)
347402 allSame = and ((zipWith (==)) ys (tail ys))
@@ -350,76 +405,190 @@ sketchSchema = sketch . toJSON
350405 (z:_) | allSame -> Just z
351406 _ -> Nothing
352407 go (Object o) = mempty
353− & type_ .~ SwaggerObject
354− & required .~ HashMap.keys o
355− & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
408+ & type_ ?~ OpenApiObject
409+ & required .~ sort (objectKeys o)
410+ & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
356411
357412-- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
358413-- Produced schema uses as much constraints as possible.
359414--
360−-- >>> encode $ sketchStrictSchema "hello"
361−-- "{\"maxLength\":5,\"pattern\":\"hello\",\"minLength\":5,\"type\":\"string\",\"enum\":[\"hello\"]}"
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+-- }
362425--
363−-- >>> encode $ sketchStrictSchema (1, 2, 3)
364−-- "{\"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]]}"
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+-- }
365469--
366−-- >>> encode $ sketchStrictSchema ("Jack", 25)
367−-- "{\"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]]}"
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+-- }
368503--
369504-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
370505-- >>> instance ToJSON Person
371−-- >>> encode $ sketchStrictSchema (Person "Jack" 25)
372−-- "{\"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\"}]}"
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+-- }
373542sketchStrictSchema :: ToJSON a => a -> Schema
374543sketchStrictSchema = go . toJSON
375544 where
376− go Null = mempty & type_ .~ SwaggerNull
545+ go Null = mempty & type_ ?~ OpenApiNull
377546 go js@(Bool _) = mempty
378− & type_ .~ SwaggerBoolean
547+ & type_ ?~ OpenApiBoolean
379548 & enum_ ?~ [js]
380549 go js@(String s) = mempty
381− & type_ .~ SwaggerString
550+ & type_ ?~ OpenApiString
382551 & maxLength ?~ fromIntegral (T.length s)
383552 & minLength ?~ fromIntegral (T.length s)
384553 & pattern ?~ s
385554 & enum_ ?~ [js]
386555 go js@(Number n) = mempty
387− & type_ .~ SwaggerNumber
556+ & type_ ?~ OpenApiNumber
388557 & maximum_ ?~ n
389558 & minimum_ ?~ n
390559 & multipleOf ?~ n
391560 & enum_ ?~ [js]
392561 go js@(Array xs) = mempty
393− & type_ .~ SwaggerArray
562+ & type_ ?~ OpenApiArray
394563 & maxItems ?~ fromIntegral sz
395564 & minItems ?~ fromIntegral sz
396− & items ?~ SwaggerItemsArray (map (Inline . go) (V.toList xs))
565+ & items ?~ OpenApiItemsArray (map (Inline . go) (V.toList xs))
397566 & uniqueItems ?~ allUnique
398567 & enum_ ?~ [js]
399568 where
400569 sz = length xs
401570 allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs))
402571 go js@(Object o) = mempty
403− & type_ .~ SwaggerObject
404− & required .~ names
405− & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
572+ & type_ ?~ OpenApiObject
573+ & required .~ sort names
574+ & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
406575 & maxProperties ?~ fromIntegral (length names)
407576 & minProperties ?~ fromIntegral (length names)
408577 & enum_ ?~ [js]
409578 where
410− names = HashMap.keys o
579+ names = objectKeys o
411580
412581class GToSchema (f :: * -> *) where
413582 gdeclareNamedSchema :: SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
414583
415−instance OVERLAPPABLE_ ToSchema a => ToSchema [a] where
584+instance {-# OVERLAPPABLE #-} ToSchema a => ToSchema [a] where
416585 declareNamedSchema _ = do
417586 ref <- declareSchemaRef (Proxy :: Proxy a)
418587 return $ unnamed $ mempty
419− & type_ .~ SwaggerArray
420− & items ?~ SwaggerItemsObject ref
588+ & type_ ?~ OpenApiArray
589+ & items ?~ OpenApiItemsObject ref
421590
422−instance OVERLAPPING_ ToSchema String where declareNamedSchema = plain . paramSchemaToSchema
591+instance {-# OVERLAPPING #-} ToSchema String where declareNamedSchema = plain . paramSchemaToSchema
423592instance ToSchema Bool where declareNamedSchema = plain . paramSchemaToSchema
424593instance ToSchema Integer where declareNamedSchema = plain . paramSchemaToSchema
425594instance ToSchema Natural where declareNamedSchema = plain . paramSchemaToSchema
@@ -442,12 +611,14 @@ instance ToSchema Scientific where declareNamedSchema = plain . paramSchemaToSc
442611instance ToSchema Double where declareNamedSchema = plain . paramSchemaToSchema
443612instance ToSchema Float where declareNamedSchema = plain . paramSchemaToSchema
444613
445−instance HasResolution a => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
614+instance (Typeable (Fixed a), HasResolution a) => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
446615
447616instance ToSchema a => ToSchema (Maybe a) where
448617 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
449618
450−instance (ToSchema a, ToSchema b) => ToSchema (Either a b)
619+instance (ToSchema a, ToSchema b) => ToSchema (Either a b) where
620+ -- To match Aeson instance
621+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions { sumEncoding = ObjectWithSingleField }
451622
452623instance ToSchema () where
453624 declareNamedSchema _ = pure (NamedSchema Nothing nullarySchema)
@@ -457,16 +628,22 @@ instance ToSchema UUID.UUID where
457628 declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p
458629 & example ?~ toJSON (UUID.toText UUID.nil)
459630
460−instance (ToSchema a, ToSchema b) => ToSchema (a, b)
461−instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c)
462−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d)
463−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e)
464−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f)
465−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g)
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
466643
467644timeSchema :: T.Text -> Schema
468645timeSchema fmt = mempty
469− & type_ .~ SwaggerString
646+ & type_ ?~ OpenApiString
470647 & format ?~ fmt
471648
472649-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
@@ -481,7 +658,7 @@ instance ToSchema LocalTime where
481658 declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss"
482659 & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0))
483660
484−-- | Format @"date"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
661+-- | Format @"date-time"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
485662instance ToSchema ZonedTime where
486663 declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time"
487664 & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))
@@ -501,8 +678,6 @@ instance ToSchema TL.Text where declareNamedSchema = plain . paramSchemaToSchema
501678
502679instance ToSchema Version where declareNamedSchema = plain . paramSchemaToSchema
503680
504−#if __GLASGOW_HASKELL__ < 800
505−#else
506681type family ToSchemaByteStringError bs where
507682 ToSchemaByteStringError bs = TypeError
508683 ( Text "Impossible to have an instance " :<>: ShowType (ToSchema bs) :<>: Text "."
@@ -511,15 +686,13 @@ type family ToSchemaByteStringError bs where
511686
512687instance ToSchemaByteStringError BS.ByteString => ToSchema BS.ByteString where declareNamedSchema = error "impossible"
513688instance ToSchemaByteStringError BSL.ByteString => ToSchema BSL.ByteString where declareNamedSchema = error "impossible"
514−#endif
515689
516690instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int))
517691
518692-- | NOTE: This schema does not account for the uniqueness of keys.
519−instance ToSchema a => ToSchema (IntMap a) where
693+instance (ToSchema a) => ToSchema (IntMap a) where
520694 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
521695
522−#if MIN_VERSION_aeson(1,0,0)
523696instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
524697 declareNamedSchema _ = case toJSONKey :: ToJSONKeyFunction k of
525698 ToJSONKeyText _ _ -> declareObjectMapSchema
@@ -528,33 +701,15 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
528701 declareObjectMapSchema = do
529702 schema <- declareSchemaRef (Proxy :: Proxy v)
530703 return $ unnamed $ mempty
531− & type_ .~ SwaggerObject
704+ & type_ ?~ OpenApiObject
532705 & additionalProperties ?~ AdditionalPropertiesSchema schema
533706
534707instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
535708 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map k v))
536709
537−#else
538−
539−instance ToSchema a => ToSchema (Map String a) where
540− declareNamedSchema _ = do
541− schema <- declareSchemaRef (Proxy :: Proxy a)
542− return $ unnamed $ mempty
543− & type_ .~ SwaggerObject
544− & additionalProperties ?~ schema
545−
546−instance ToSchema a => ToSchema (Map T.Text a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map String a))
547−instance ToSchema a => ToSchema (Map TL.Text a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map String a))
548−
549−instance ToSchema a => ToSchema (HashMap String a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map String a))
550−instance ToSchema a => ToSchema (HashMap T.Text a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map String a))
551−instance ToSchema a => ToSchema (HashMap TL.Text a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map String a))
552−
553−#endif
554−
555−instance OVERLAPPING_ ToSchema Object where
710+instance {-# OVERLAPPING #-} ToSchema Object where
556711 declareNamedSchema _ = pure $ NamedSchema (Just "Object") $ mempty
557− & type_ .~ SwaggerObject
712+ & type_ ?~ OpenApiObject
558713 & description ?~ "Arbitrary JSON object."
559714 & additionalProperties ?~ AdditionalPropertiesAllowed True
560715
@@ -591,11 +746,15 @@ instance ToSchema a => ToSchema (Identity a) where declareNamedSchema _ = declar
591746
592747-- | Default schema for @'Bounded'@, @'Integral'@ types.
593748--
594−-- >>> encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
595−-- "{\"maximum\":32767,\"minimum\":-32768,\"type\":\"integer\"}"
749+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
750+-- {
751+-- "maximum": 32767,
752+-- "minimum": -32768,
753+-- "type": "integer"
754+-- }
596755toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
597756toSchemaBoundedIntegral _ = mempty
598− & type_ .~ SwaggerInteger
757+ & type_ ?~ OpenApiInteger
599758 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
600759 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
601760
@@ -624,8 +783,27 @@ genericDeclareNamedSchemaNewtype opts f proxy = genericNameSchema opts proxy <$>
624783-- >>> instance ToSchema ButtonState
625784-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
626785-- >>> type ImageUrl = T.Text
627−-- >>> encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
628−-- "{\"properties\":{\"Neutral\":{\"type\":\"string\"},\"Focus\":{\"type\":\"string\"},\"Active\":{\"type\":\"string\"},\"Hover\":{\"type\":\"string\"},\"Disabled\":{\"type\":\"string\"}},\"type\":\"object\"}"
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+-- }
629807--
630808-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
631809-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -633,15 +811,15 @@ declareSchemaBoundedEnumKeyMapping :: forall map key value.
633811 (Bounded key, Enum key, ToJSONKey key, ToSchema key, ToSchema value)
634812 => Proxy (map key value) -> Declare (Definitions Schema) Schema
635813declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key of
636− ToJSONKeyText keyToText _ -> objectSchema keyToText
814+ ToJSONKeyText getKey _ -> objectSchema getKey
637815 ToJSONKeyValue _ _ -> declareSchema (Proxy :: Proxy [(key, value)])
638816 where
639− objectSchema keyToText = do
817+ objectSchema getKey = do
640818 valueRef <- declareSchemaRef (Proxy :: Proxy value)
641819 let allKeys = [minBound..maxBound :: key]
642− mkPair k = (keyToText k, valueRef)
820+ mkPair k = (keyToText $ getKey k, valueRef)
643821 return $ mempty
644− & type_ .~ SwaggerObject
822+ & type_ ?~ OpenApiObject
645823 & properties .~ InsOrdHashMap.fromList (map mkPair allKeys)
646824
647825-- | A 'Schema' for a mapping with 'Bounded' 'Enum' keys.
@@ -652,8 +830,27 @@ declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key o
652830-- >>> instance ToSchema ButtonState
653831-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
654832-- >>> type ImageUrl = T.Text
655−-- >>> encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
656−-- "{\"properties\":{\"Neutral\":{\"type\":\"string\"},\"Focus\":{\"type\":\"string\"},\"Active\":{\"type\":\"string\"},\"Hover\":{\"type\":\"string\"},\"Disabled\":{\"type\":\"string\"}},\"type\":\"object\"}"
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+-- }
657854--
658855-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
659856-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -663,32 +860,32 @@ toSchemaBoundedEnumKeyMapping :: forall map key value.
663860toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping
664861
665862-- | A configurable generic @'Schema'@ creator.
666−genericDeclareSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareSchemaUnrestricted") =>
863+genericDeclareSchema :: (Generic a, GToSchema (Rep a), Typeable a) =>
667864 SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
668−genericDeclareSchema = genericDeclareSchemaUnrestricted
865+genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchema opts proxy
669866
670867-- | A configurable generic @'NamedSchema'@ creator.
671868-- This function applied to @'defaultSchemaOptions'@
672869-- is used as the default for @'declareNamedSchema'@
673870-- when the type is an instance of @'Generic'@.
674−genericDeclareNamedSchema :: (Generic a, GToSchema (Rep a), TypeHasSimpleShape a "genericDeclareNamedSchemaUnrestricted") =>
675− SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
676−genericDeclareNamedSchema = genericDeclareNamedSchemaUnrestricted
677−
678−-- | A configurable generic @'Schema'@ creator.
679871--
680−-- Unlike 'genericDeclareSchema' also works for mixed sum types.
681−-- Use with care since some Swagger tools do not support well schemas for mixed sum types.
682−genericDeclareSchemaUnrestricted :: (Generic a, GToSchema (Rep a)) => SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
683−genericDeclareSchemaUnrestricted opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchemaUnrestricted opts proxy
684−
685−-- | A configurable generic @'NamedSchema'@ creator.
872+-- Default implementation will use the name from 'Typeable' instance, including concrete
873+-- instantioations of type variables.
874+--
875+-- For example:
686876--
687−-- Unlike 'genericDeclareNamedSchema' also works for mixed sum types.
688−-- Use with care since some Swagger tools do not support well schemas for mixed sum types.
689−genericDeclareNamedSchemaUnrestricted :: forall a. (Generic a, GToSchema (Rep a)) =>
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) =>
690880 SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
691−genericDeclareNamedSchemaUnrestricted opts _ = gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
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+
692889
693890-- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'.
694891genericNameSchema :: forall a d f.
@@ -704,19 +901,19 @@ gdatatypeSchemaName opts _ = case orig of
704901 orig = datatypeName (Proxy3 :: Proxy3 d f a)
705902 name = datatypeNameModifier opts orig
706903
707−-- | Lift a plain @'ParamSchema'@ into a model @'NamedSchema'@.
904+-- | Construct 'NamedSchema' usinng 'ToParamSchema'.
708905paramSchemaToNamedSchema :: (ToParamSchema a, Generic a, Rep a ~ D1 d f, Datatype d) =>
709906 SchemaOptions -> Proxy a -> NamedSchema
710907paramSchemaToNamedSchema opts proxy = genericNameSchema opts proxy (paramSchemaToSchema proxy)
711908
712−-- | Lift a plain @'ParamSchema'@ into a model @'Schema'@.
909+-- | Construct 'Schema' usinng 'ToParamSchema'.
713910paramSchemaToSchema :: ToParamSchema a => Proxy a -> Schema
714−paramSchemaToSchema proxy = mempty & paramSchema .~ toParamSchema proxy
911+paramSchemaToSchema = toParamSchema
715912
716913nullarySchema :: Schema
717914nullarySchema = mempty
718− & type_ .~ SwaggerArray
719− & items ?~ SwaggerItemsArray []
915+ & type_ ?~ OpenApiArray
916+ & items ?~ OpenApiItemsArray []
720917
721918gtoNamedSchema :: GToSchema f => SchemaOptions -> Proxy f -> NamedSchema
722919gtoNamedSchema opts proxy = undeclare $ gdeclareNamedSchema opts proxy mempty
@@ -734,10 +931,10 @@ instance (Datatype d, GToSchema f) => GToSchema (D1 d f) where
734931 where
735932 name = gdatatypeSchemaName opts (Proxy :: Proxy d)
736933
737−instance OVERLAPPABLE_ GToSchema f => GToSchema (C1 c f) where
934+instance {-# OVERLAPPABLE #-} GToSchema f => GToSchema (C1 c f) where
738935 gdeclareNamedSchema opts _ = gdeclareNamedSchema opts (Proxy :: Proxy f)
739936
740−instance OVERLAPPING_ Constructor c => GToSchema (C1 c U1) where
937+instance {-# OVERLAPPING #-} Constructor c => GToSchema (C1 c U1) where
741938 gdeclareNamedSchema = gdeclareNamedSumSchema
742939
743940-- | Single field constructor.
@@ -746,12 +943,15 @@ instance (Selector s, GToSchema f, GToSchema (S1 s f)) => GToSchema (C1 c (S1 s
746943 | unwrapUnaryRecords opts = fieldSchema
747944 | otherwise =
748945 case schema ^. items of
749− Just (SwaggerItemsArray [_]) -> fieldSchema
946+ Just (OpenApiItemsArray [_]) -> fieldSchema
750947 _ -> do
751− declare defs
752− return (unnamed schema)
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')
753953 where
754− (defs, NamedSchema _ schema) = runDeclare recordSchema mempty
954+ (_, NamedSchema _ schema) = runDeclare recordSchema mempty
755955 recordSchema = gdeclareNamedSchema opts (Proxy :: Proxy (S1 s f)) s
756956 fieldSchema = gdeclareNamedSchema opts (Proxy :: Proxy f) s
757957
@@ -774,10 +974,10 @@ gdeclareSchemaRef opts proxy = do
774974 return $ Ref (Reference name)
775975 _ -> Inline <$> gdeclareSchema opts proxy
776976
777−appendItem :: Referenced Schema -> Maybe (SwaggerItems 'SwaggerKindSchema) -> Maybe (SwaggerItems 'SwaggerKindSchema)
778−appendItem x Nothing = Just (SwaggerItemsArray [x])
779−appendItem x (Just (SwaggerItemsArray xs)) = Just (SwaggerItemsArray (xs ++ [x]))
780−appendItem _ _ = error "GToSchema.appendItem: cannot append to SwaggerItemsObject"
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"
781981
782982withFieldSchema :: forall proxy s f. (Selector s, GToSchema f) =>
783983 SchemaOptions -> proxy s f -> Bool -> Schema -> Declare (Definitions Schema) Schema
@@ -786,12 +986,12 @@ withFieldSchema opts _ isRequiredField schema = do
786986 return $
787987 if T.null fname
788988 then schema
789− & type_ .~ SwaggerArray
989+ & type_ ?~ OpenApiArray
790990 & items %~ appendItem ref
791991 & maxItems %~ Just . maybe 1 (+1) -- increment maxItems
792992 & minItems %~ Just . maybe 1 (+1) -- increment minItems
793993 else schema
794− & type_ .~ SwaggerObject
994+ & type_ ?~ OpenApiObject
795995 & properties . at fname ?~ ref
796996 & if isRequiredField
797997 then required %~ (++ [fname])
@@ -800,79 +1000,127 @@ withFieldSchema opts _ isRequiredField schema = do
8001000 fname = T.pack (fieldLabelModifier opts (selName (Proxy3 :: Proxy3 s f p)))
8011001
8021002-- | Optional record fields.
803−instance OVERLAPPING_ (Selector s, ToSchema c) => GToSchema (S1 s (K1 i (Maybe c))) where
1003+instance {-# OVERLAPPING #-} (Selector s, ToSchema c) => GToSchema (S1 s (K1 i (Maybe c))) where
8041004 gdeclareNamedSchema opts _ = fmap unnamed . withFieldSchema opts (Proxy2 :: Proxy2 s (K1 i (Maybe c))) False
8051005
8061006-- | Record fields.
807−instance OVERLAPPABLE_ (Selector s, GToSchema f) => GToSchema (S1 s f) where
1007+instance {-# OVERLAPPABLE #-} (Selector s, GToSchema f) => GToSchema (S1 s f) where
8081008 gdeclareNamedSchema opts _ = fmap unnamed . withFieldSchema opts (Proxy2 :: Proxy2 s f) True
8091009
810−instance OVERLAPPING_ ToSchema c => GToSchema (K1 i (Maybe c)) where
1010+instance {-# OVERLAPPING #-} ToSchema c => GToSchema (K1 i (Maybe c)) where
8111011 gdeclareNamedSchema _ _ _ = declareNamedSchema (Proxy :: Proxy c)
8121012
813−instance OVERLAPPABLE_ ToSchema c => GToSchema (K1 i c) where
1013+instance {-# OVERLAPPABLE #-} ToSchema c => GToSchema (K1 i c) where
8141014 gdeclareNamedSchema _ _ _ = declareNamedSchema (Proxy :: Proxy c)
8151015
8161016instance ( GSumToSchema f
8171017 , GSumToSchema g
8181018 ) => GToSchema (f :+: g)
8191019 where
820− gdeclareNamedSchema = gdeclareNamedSumSchema
1020+ -- Aeson does not unwrap unary record in sum types.
1021+ gdeclareNamedSchema opts p s = gdeclareNamedSumSchema (opts { unwrapUnaryRecords = False } )p s
8211022
8221023gdeclareNamedSumSchema :: GSumToSchema f => SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
823−gdeclareNamedSumSchema opts proxy s
824− | allNullaryToStringTag opts && allNullary = pure $ unnamed (toStringTag sumSchema)
825− | otherwise = (unnamed . fst) <$> runWriterT declareSumSchema
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)
8261031 where
827− declareSumSchema = gsumToSchema opts proxy s
828− (sumSchema, All allNullary) = undeclare (runWriterT declareSumSchema)
1032+ declareSumSchema = gsumToSchema opts proxy
1033+ (sumSchemas, All allNullary) = undeclare (runWriterT declareSumSchema)
8291034
830− toStringTag schema = mempty
831− & type_ .~ SwaggerString
832− & enum_ ?~ map toJSON (schema ^.. properties.ifolded.asIndex)
1035+ toStringTag schemas = mempty
1036+ & type_ ?~ OpenApiString
1037+ & enum_ ?~ map (String . fst) sumSchemas
8331038
8341039type AllNullary = All
8351040
8361041class GSumToSchema (f :: * -> *) where
837− gsumToSchema :: SchemaOptions -> Proxy f -> Schema -> WriterT AllNullary (Declare (Definitions Schema)) Schema
1042+ gsumToSchema :: SchemaOptions -> Proxy f -> WriterT AllNullary (Declare (Definitions Schema)) [(T.Text, Referenced Schema)]
8381043
8391044instance (GSumToSchema f, GSumToSchema g) => GSumToSchema (f :+: g) where
840− gsumToSchema opts _ = gsumToSchema opts (Proxy :: Proxy f) >=> gsumToSchema opts (Proxy :: Proxy g)
1045+ gsumToSchema opts _ =
1046+ (<>) <$> gsumToSchema opts (Proxy :: Proxy f) <*> gsumToSchema opts (Proxy :: Proxy g)
8411047
1048+-- | Convert one component of the sum to schema, to be later combined with @oneOf@.
8421049gsumConToSchemaWith :: forall c f. (GToSchema (C1 c f), Constructor c) =>
843− Referenced Schema -> SchemaOptions -> Proxy (C1 c f) -> Schema -> Schema
844−gsumConToSchemaWith ref opts _ schema = schema
845− & type_ .~ SwaggerObject
846− & properties . at tag ?~ ref
847− & maxProperties ?~ 1
848− & minProperties ?~ 1
1050+ Maybe (Referenced Schema) -> SchemaOptions -> Proxy (C1 c f) -> (T.Text, Referenced Schema)
1051+gsumConToSchemaWith ref opts _ = (tag, schema)
8491052 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+
8501089 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
8511093
8521094gsumConToSchema :: (GToSchema (C1 c f), Constructor c) =>
853− SchemaOptions -> Proxy (C1 c f) -> Schema -> Declare (Definitions Schema) Schema
854−gsumConToSchema opts proxy schema = do
1095+ SchemaOptions -> Proxy (C1 c f) -> Declare (Definitions Schema) [(T.Text, Referenced Schema)]
1096+gsumConToSchema opts proxy = do
8551097 ref <- gdeclareSchemaRef opts proxy
856− return $ gsumConToSchemaWith ref opts proxy schema
1098+ return [gsumConToSchemaWith (Just ref) opts proxy]
8571099
858−instance OVERLAPPABLE_ (Constructor c, GToSchema f) => GSumToSchema (C1 c f) where
859− gsumToSchema opts proxy schema = do
1100+instance {-# OVERLAPPABLE #-} (Constructor c, GToSchema f) => GSumToSchema (C1 c f) where
1101+ gsumToSchema opts proxy = do
8601102 tell (All False)
861− lift $ gsumConToSchema opts proxy schema
1103+ lift $ gsumConToSchema opts proxy
8621104
8631105instance (Constructor c, Selector s, GToSchema f) => GSumToSchema (C1 c (S1 s f)) where
864− gsumToSchema opts proxy schema = do
1106+ gsumToSchema opts proxy = do
8651107 tell (All False)
866− lift $ gsumConToSchema opts proxy schema
1108+ lift $ gsumConToSchema opts proxy
8671109
8681110instance Constructor c => GSumToSchema (C1 c U1) where
869− gsumToSchema opts proxy = pure . gsumConToSchemaWith (Inline nullarySchema) opts proxy
1111+ gsumToSchema opts proxy = pure $ (:[]) $ gsumConToSchemaWith Nothing opts proxy
8701112
8711113data Proxy2 a b = Proxy2
8721114
8731115data Proxy3 a b c = Proxy3
8741116
875−-- $setup
876−-- >>> import Data.Swagger
877−-- >>> import Data.Aeson (encode)
878−-- >>> import Data.Aeson.Types (toJSONKeyText)
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+-}
src/Data/Swagger/Internal/Schema/Validation.hs → src/Data/OpenApi/Internal/Schema/Validation.hsrenamed+218−73
@@ -1,3 +1,5 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE CPP #-}
13{-# LANGUAGE DataKinds #-}
24{-# LANGUAGE DeriveFunctor #-}
35{-# LANGUAGE FlexibleInstances #-}
@@ -11,39 +13,51 @@
1113{-# LANGUAGE ScopedTypeVariables #-}
1214{-# LANGUAGE TemplateHaskell #-}
1315{-# LANGUAGE UndecidableInstances #-}
16+{-# LANGUAGE ViewPatterns #-}
1417-- |
15−-- Module: Data.Swagger.Internal.Schema.Validation
18+-- Module: Data.OpenApi.Internal.Schema.Validation
1619-- Copyright: (c) 2015 GetShopTV
1720-- License: BSD3
1821-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
1922-- Stability: experimental
2023--
2124-- Validate JSON values with Swagger Schema.
22−module Data.Swagger.Internal.Schema.Validation where
25+module Data.OpenApi.Internal.Schema.Validation where
26+
27+import Prelude ()
28+import Prelude.Compat
2329
2430import Control.Applicative
25−import Control.Lens
26−import Control.Monad (when)
31+import Control.Lens hiding (allOf)
32+import Control.Monad (forM, forM_, when)
2733
2834import Data.Aeson hiding (Result)
35+#if MIN_VERSION_aeson(2,0,0)
36+import qualified Data.Aeson.KeyMap as KeyMap
37+#endif
2938import Data.Foldable (for_, sequenceA_,
3039 traverse_)
40+#if !MIN_VERSION_aeson(2,0,0)
3141import Data.HashMap.Strict (HashMap)
32−import qualified Data.HashMap.Strict as HashMap
42+#endif
3343import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
3444import qualified "unordered-containers" Data.HashSet as HashSet
35−import Data.Monoid
45+import Data.Maybe (fromMaybe)
3646import Data.Proxy
3747import Data.Scientific (Scientific, isInteger)
3848import Data.Text (Text)
3949import qualified Data.Text as Text
50+import qualified Data.Text.Lazy as TL
51+import qualified Data.Text.Lazy.Encoding as TL
4052import Data.Vector (Vector)
4153import qualified Data.Vector as Vector
4254
43−import Data.Swagger.Declare
44−import Data.Swagger.Internal
45−import Data.Swagger.Internal.Schema
46−import Data.Swagger.Lens
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
4761
4862-- | Validate @'ToJSON'@ instance matches @'ToSchema'@ for a given value.
4963-- This can be used with QuickCheck to ensure those instances are coherent:
@@ -52,18 +66,101 @@ import Data.Swagger.Lens
5266--
5367-- /NOTE:/ @'validateToJSON'@ does not perform string pattern validation.
5468-- See @'validateToJSONWithPatternChecker'@.
69+--
70+-- See 'renderValidationErrors' on how the output is structured.
71+validatePrettyToJSON :: forall a. (ToJSON a, ToSchema a) => a -> Maybe String
72+validatePrettyToJSON = renderValidationErrors validateToJSON
73+
74+-- | Variant of 'validatePrettyToJSON' with typed output.
5575validateToJSON :: forall a. (ToJSON a, ToSchema a) => a -> [ValidationError]
5676validateToJSON = validateToJSONWithPatternChecker (\_pattern _str -> True)
5777
5878-- | Validate @'ToJSON'@ instance matches @'ToSchema'@ for a given value and pattern checker.
5979-- This can be used with QuickCheck to ensure those instances are coherent.
6080--
61−-- For validation without patterns see @'validateToJSON'@.
81+-- For validation without patterns see @'validateToJSON'@. See also:
82+-- 'renderValidationErrors'.
6283validateToJSONWithPatternChecker :: forall a. (ToJSON a, ToSchema a) => (Pattern -> Text -> Bool) -> a -> [ValidationError]
6384validateToJSONWithPatternChecker checker = validateJSONWithPatternChecker checker defs sch . toJSON
6485 where
6586 (defs, sch) = runDeclare (declareSchema (Proxy :: Proxy a)) mempty
6687
88+-- | Pretty print validation errors
89+-- together with actual JSON and Swagger Schema
90+-- (using 'encodePretty').
91+--
92+-- >>> import Data.Aeson as Aeson
93+-- >>> import Data.Foldable (traverse_)
94+-- >>> import GHC.Generics
95+-- >>> data Phone = Phone { value :: String } deriving (Generic)
96+-- >>> data Person = Person { name :: String, phone :: Phone } deriving (Generic)
97+-- >>> instance ToJSON Person where toJSON p = object [ "name" Aeson..= name p ]
98+-- >>> instance ToSchema Phone
99+-- >>> instance ToSchema Person
100+-- >>> let person = Person { name = "John", phone = Phone "123456" }
101+-- >>> traverse_ putStrLn $ renderValidationErrors validateToJSON person
102+-- Validation against the schema fails:
103+-- * property "phone" is required, but not found in "{\"name\":\"John\"}"
104+-- <BLANKLINE>
105+-- JSON value:
106+-- {
107+-- "name": "John"
108+-- }
109+-- <BLANKLINE>
110+-- Swagger Schema:
111+-- {
112+-- "properties": {
113+-- "name": {
114+-- "type": "string"
115+-- },
116+-- "phone": {
117+-- "$ref": "#/components/schemas/Phone"
118+-- }
119+-- },
120+-- "required": [
121+-- "name",
122+-- "phone"
123+-- ],
124+-- "type": "object"
125+-- }
126+-- <BLANKLINE>
127+-- Swagger Description Context:
128+-- {
129+-- "Phone": {
130+-- "properties": {
131+-- "value": {
132+-- "type": "string"
133+-- }
134+-- },
135+-- "required": [
136+-- "value"
137+-- ],
138+-- "type": "object"
139+-- }
140+-- }
141+-- <BLANKLINE>
142+renderValidationErrors
143+ :: forall a. (ToJSON a, ToSchema a)
144+ => (a -> [ValidationError]) -> a -> Maybe String
145+renderValidationErrors f x =
146+ case f x of
147+ [] -> Nothing
148+ errors -> Just $ unlines
149+ [ "Validation against the schema fails:"
150+ , unlines (map (" * " ++) errors)
151+ , "JSON value:"
152+ , ppJSONString (toJSON x)
153+ , ""
154+ , "Swagger Schema:"
155+ , ppJSONString (toJSON schema_)
156+ , ""
157+ , "Swagger Description Context:"
158+ , ppJSONString (toJSON refs_)
159+ ]
160+ where
161+ ppJSONString = TL.unpack . TL.decodeUtf8 . encodePretty
162+ (refs_, schema_) = runDeclare (declareSchema (Proxy :: Proxy a)) mempty
163+
67164-- | Validate JSON @'Value'@ against Swagger @'Schema'@.
68165--
69166-- prop> validateJSON mempty (toSchema (Proxy :: Proxy Int)) (toJSON (x :: Int)) == []
@@ -114,7 +211,7 @@ instance Monad Result where
114211
115212-- | Validation configuration.
116213data Config = Config
117− { -- | Pattern checker for @'_paramSchemaPattern'@ validation.
214+ { -- | Pattern checker for @'_schemaPattern'@ validation.
118215 configPatternChecker :: Pattern -> Text -> Bool
119216 -- | Schema definitions in scope to resolve references.
120217 , configDefinitions :: Definitions Schema
@@ -207,40 +304,34 @@ validateWithSchemaRef (Inline s) js = sub s (validateWithSchema js)
207304
208305-- | Validate JSON @'Value'@ with Swagger @'Schema'@.
209306validateWithSchema :: Value -> Validation Schema ()
210−validateWithSchema value = do
211− validateSchemaType value
212− sub_ paramSchema $ validateEnum value
213−
214−-- | Validate JSON @'Value'@ with Swagger @'ParamSchema'@.
215−validateWithParamSchema :: Value -> Validation (ParamSchema t) ()
216−validateWithParamSchema value = do
217− validateParamSchemaType value
218− validateEnum value
307+validateWithSchema val = do
308+ validateSchemaType val
309+ validateEnum val
219310
220−validateInteger :: Scientific -> Validation (ParamSchema t) ()
311+validateInteger :: Scientific -> Validation Schema ()
221312validateInteger n = do
222313 when (not (isInteger n)) $
223314 invalid ("not an integer")
224315 validateNumber n
225316
226−validateNumber :: Scientific -> Validation (ParamSchema t) ()
317+validateNumber :: Scientific -> Validation Schema ()
227318validateNumber n = withConfig $ \_cfg -> withSchema $ \sch -> do
228319 let exMax = Just True == sch ^. exclusiveMaximum
229320 exMin = Just True == sch ^. exclusiveMinimum
230321
231322 check maximum_ $ \m ->
232323 when (if exMax then (n >= m) else (n > m)) $
233− invalid ("value " ++ show n ++ " exceeds maximum (should be " ++ if exMax then "<" else "<=" ++ show m ++ ")")
324+ invalid ("value " ++ show n ++ " exceeds maximum (should be " ++ (if exMax then "<" else "<=") ++ show m ++ ")")
234325
235326 check minimum_ $ \m ->
236327 when (if exMin then (n <= m) else (n < m)) $
237− invalid ("value " ++ show n ++ " falls below minimum (should be " ++ if exMin then ">" else ">=" ++ show m ++ ")")
328+ invalid ("value " ++ show n ++ " falls below minimum (should be " ++ (if exMin then ">" else ">=") ++ show m ++ ")")
238329
239330 check multipleOf $ \k ->
240331 when (not (isInteger (n / k))) $
241332 invalid ("expected a multiple of " ++ show k ++ " but got " ++ show n)
242333
243−validateString :: Text -> Validation (ParamSchema t) ()
334+validateString :: Text -> Validation Schema ()
244335validateString s = do
245336 check maxLength $ \n ->
246337 when (len > fromInteger n) $
@@ -257,7 +348,7 @@ validateString s = do
257348 where
258349 len = Text.length s
259350
260−validateArray :: Vector Value -> Validation (ParamSchema t) ()
351+validateArray :: Vector Value -> Validation Schema ()
261352validateArray xs = do
262353 check maxItems $ \n ->
263354 when (len > fromInteger n) $
@@ -268,9 +359,8 @@ validateArray xs = do
268359 invalid ("array is too short (size should be >=" ++ show n ++ ")")
269360
270361 check items $ \case
271− SwaggerItemsPrimitive _ itemSchema -> sub itemSchema $ traverse_ validateWithParamSchema xs
272− SwaggerItemsObject itemSchema -> traverse_ (validateWithSchemaRef itemSchema) xs
273− SwaggerItemsArray itemSchemas -> do
362+ OpenApiItemsObject itemSchema -> traverse_ (validateWithSchemaRef itemSchema) xs
363+ OpenApiItemsArray itemSchemas -> do
274364 when (len /= length itemSchemas) $
275365 invalid ("array size is invalid (should be exactly " ++ show (length itemSchemas) ++ ")")
276366 sequenceA_ (zipWith validateWithSchemaRef itemSchemas (Vector.toList xs))
@@ -282,11 +372,20 @@ validateArray xs = do
282372 len = Vector.length xs
283373 allUnique = len == HashSet.size (HashSet.fromList (Vector.toList xs))
284374
285−validateObject :: HashMap Text Value -> Validation Schema ()
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 ()
286382validateObject o = withSchema $ \sch ->
287383 case sch ^. discriminator of
288− Just pname -> case fromJSON <$> HashMap.lookup pname o of
289− Just (Success ref) -> validateWithSchemaRef ref (Object o)
384+ Just (Discriminator pname types) -> case fromJSON <$> lookupKey pname o of
385+ Just (Success pvalue) ->
386+ let ref = fromMaybe pvalue $ InsOrdHashMap.lookup pvalue types
387+ -- TODO ref may be name or reference
388+ in validateWithSchemaRef (Ref (Reference ref)) (Object o)
290389 Just (Error msg) -> invalid ("failed to parse discriminator property " ++ show pname ++ ": " ++ show msg)
291390 Nothing -> invalid ("discriminator property " ++ show pname ++ "is missing")
292391 Nothing -> do
@@ -301,15 +400,15 @@ validateObject o = withSchema $ \sch ->
301400 validateRequired
302401 validateProps
303402 where
304− size = fromIntegral (HashMap.size o)
403+ size = fromIntegral (length o)
305404
306405 validateRequired = withSchema $ \sch -> traverse_ validateReq (sch ^. required)
307406 validateReq n =
308− when (not (HashMap.member n o)) $
407+ when (not (hasKey n o)) $
309408 invalid ("property " ++ show n ++ " is required, but not found in " ++ show (encode o))
310409
311410 validateProps = withSchema $ \sch -> do
312− for_ (HashMap.toList o) $ \(k, v) ->
411+ for_ (objectToList o) $ \(keyToText -> k, v) ->
313412 case v of
314413 Null | not (k `elem` (sch ^. required)) -> valid -- null is fine for non-required property
315414 _ ->
@@ -322,22 +421,24 @@ validateObject o = withSchema $ \sch ->
322421 validateAdditional _ v (AdditionalPropertiesSchema s) = validateWithSchemaRef s v
323422
324423 unknownProperty :: Text -> Validation s a
325− unknownProperty name = invalid $
326− "property " <> show name <> " is found in JSON value, but it is not mentioned in Swagger schema"
424+ unknownProperty pname = invalid $
425+ "property " <> show pname <> " is found in JSON value, but it is not mentioned in Swagger schema"
327426
328−validateEnum :: Value -> Validation (ParamSchema t) ()
329−validateEnum value = do
427+validateEnum :: Value -> Validation Schema ()
428+validateEnum val = do
330429 check enum_ $ \xs ->
331− when (value `notElem` xs) $
332− invalid ("expected one of " ++ show (encode xs) ++ " but got " ++ show value)
430+ when (val `notElem` xs) $
431+ invalid ("expected one of " ++ show (encode xs) ++ " but got " ++ show val)
333432
334433-- | Infer schema type based on used properties.
335434--
336−-- >>> inferSchemaTypes <$> decode "{\"minLength\": 2}"
337−-- Just [SwaggerString]
338−inferSchemaTypes :: Schema -> [SwaggerType 'SwaggerKindSchema]
339−inferSchemaTypes sch = inferParamSchemaTypes (sch ^. paramSchema) ++
340− [ SwaggerObject | any ($ sch)
435+-- This is like 'inferParamSchemaTypes', but also works for objects:
436+--
437+-- >>> inferSchemaTypes <$> decode "{\"minProperties\": 1}"
438+-- Just [OpenApiObject]
439+inferSchemaTypes :: Schema -> [OpenApiType]
440+inferSchemaTypes sch = inferParamSchemaTypes sch ++
441+ [ OpenApiObject | any ($ sch)
341442 [ has (additionalProperties._Just)
342443 , has (maxProperties._Just)
343444 , has (minProperties._Just)
@@ -347,45 +448,89 @@ inferSchemaTypes sch = inferParamSchemaTypes (sch ^. paramSchema) ++
347448-- | Infer schema type based on used properties.
348449--
349450-- >>> inferSchemaTypes <$> decode "{\"minLength\": 2}"
350−-- Just [SwaggerString]
351−inferParamSchemaTypes :: ParamSchema t -> [SwaggerType t]
451+-- Just [OpenApiString]
452+--
453+-- >>> inferSchemaTypes <$> decode "{\"maxItems\": 0}"
454+-- Just [OpenApiArray]
455+--
456+-- From numeric properties 'OpenApiInteger' type is inferred.
457+-- If you want 'OpenApiNumber' instead, you must specify it explicitly.
458+--
459+-- >>> inferSchemaTypes <$> decode "{\"minimum\": 1}"
460+-- Just [OpenApiInteger]
461+inferParamSchemaTypes :: Schema -> [OpenApiType]
352462inferParamSchemaTypes sch = concat
353− [ [ SwaggerArray | any ($ sch)
463+ [ [ OpenApiArray | any ($ sch)
354464 [ has (items._Just)
355465 , has (maxItems._Just)
356466 , has (minItems._Just)
357467 , has (uniqueItems._Just) ] ]
358− , [ SwaggerInteger | any ($ sch)
468+ , [ OpenApiInteger | any ($ sch)
359469 [ has (exclusiveMaximum._Just)
360470 , has (exclusiveMinimum._Just)
361471 , has (maximum_._Just)
362472 , has (minimum_._Just)
363473 , has (multipleOf._Just) ] ]
364− , [ SwaggerString | any ($ sch)
474+ , [ OpenApiString | any ($ sch)
365475 [ has (maxLength._Just)
366476 , has (minLength._Just)
367477 , has (pattern._Just) ] ]
368478 ]
369479
370480validateSchemaType :: Value -> Validation Schema ()
371−validateSchemaType value = withSchema $ \sch ->
372− case (sch ^. type_, value) of
373− (SwaggerNull, Null) -> valid
374− (SwaggerBoolean, Bool _) -> valid
375− (SwaggerInteger, Number n) -> sub_ paramSchema (validateInteger n)
376− (SwaggerNumber, Number n) -> sub_ paramSchema (validateNumber n)
377− (SwaggerString, String s) -> sub_ paramSchema (validateString s)
378− (SwaggerArray, Array xs) -> sub_ paramSchema (validateArray xs)
379− (SwaggerObject, Object o) -> validateObject o
380− (t, _) -> invalid $ "expected JSON value of type " ++ show t
381−
382−validateParamSchemaType :: Value -> Validation (ParamSchema t) ()
383−validateParamSchemaType value = withSchema $ \sch ->
384− case (sch ^. type_, value) of
385− (SwaggerBoolean, Bool _) -> valid
386− (SwaggerInteger, Number n) -> validateInteger n
387− (SwaggerNumber, Number n) -> validateNumber n
388− (SwaggerString, String s) -> validateString s
389− (SwaggerArray, Array xs) -> validateArray xs
390− (t, _) -> invalid $ "expected JSON value of type " ++ show t
391−
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
522+ (Nothing, Bool _) -> valid
523+ -- Number by default
524+ (Nothing, Number n) -> validateNumber n
525+ (Nothing, String s) -> validateString s
526+ (Nothing, Array xs) -> validateArray xs
527+ bad -> invalid $ "expected JSON value of type " ++ showType bad
528+
529+showType :: (Maybe OpenApiType, Value) -> String
530+showType (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"
src/Data/Swagger/Internal/TypeShape.hs → src/Data/OpenApi/Internal/TypeShape.hsrenamed+1−11
@@ -1,12 +1,10 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE ConstraintKinds #-}
32{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE KindSignatures #-}
53{-# LANGUAGE TypeFamilies #-}
64{-# LANGUAGE TypeOperators #-}
75{-# LANGUAGE UndecidableInstances #-}
86
9−module Data.Swagger.Internal.TypeShape where
7+module Data.OpenApi.Internal.TypeShape where
108
119import Data.Proxy
1210import GHC.Generics
@@ -37,11 +35,6 @@ type family TypeHasSimpleShape t (f :: Symbol) :: Constraint where
3735type family GenericHasSimpleShape t (f :: Symbol) (s :: TypeShape) :: Constraint where
3836 GenericHasSimpleShape t f Enumeration = ()
3937 GenericHasSimpleShape t f SumOfProducts = ()
40−#if __GLASGOW_HASKELL__ < 800
41− GenericHasSimpleShape t f Mixed = CannotDeriveSchemaForMixedSumType t
42−
43−class CannotDeriveSchemaForMixedSumType t where
44−#else
4538 GenericHasSimpleShape t f Mixed =
4639 TypeError
4740 ( Text "Cannot derive Generic-based Swagger Schema for " :<>: ShowType t
@@ -51,7 +44,6 @@ class CannotDeriveSchemaForMixedSumType t where
5144 :$$: Text "that matches aeson's Generic-based toJSON,"
5245 :$$: Text "but that's not supported by some Swagger tools."
5346 )
54−#endif
5547
5648-- | Infer a 'TypeShape' for a generic representation of a type.
5749type family GenericShape (g :: * -> *) :: TypeShape
@@ -62,5 +54,3 @@ type instance GenericShape (D1 d f) = GenericShape f
6254type instance GenericShape (C1 c U1) = Enumeration
6355type instance GenericShape (C1 c (S1 s f)) = SumOfProducts
6456type instance GenericShape (C1 c (f :*: g)) = SumOfProducts
65−
66−
src/Data/Swagger/Internal/Utils.hs → src/Data/OpenApi/Internal/Utils.hsrenamed+7−1
@@ -4,7 +4,7 @@
44{-# LANGUAGE OverloadedStrings #-}
55{-# LANGUAGE RankNTypes #-}
66{-# LANGUAGE TypeOperators #-}
7−module Data.Swagger.Internal.Utils where
7+module Data.OpenApi.Internal.Utils where
88
99import Prelude ()
1010import Prelude.Compat
@@ -13,6 +13,8 @@ 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
1618import Data.Char
1719import Data.Data
1820import Data.Hashable (Hashable)
@@ -44,6 +46,7 @@ swaggerFieldRules = defaultFieldRules & lensField %~ swaggerFieldNamer
4446 fixName' "maximum" = "maximum_" -- Prelude conflict
4547 fixName' "enum" = "enum_" -- Control.Lens conflict
4648 fixName' "head" = "head_" -- Prelude conflict
49+ fixName' "not" = "not_" -- Prelude conflict
4750 fixName' n = n
4851
4952gunfoldEnum :: String -> [a] -> (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c a
@@ -131,3 +134,6 @@ instance SwaggerMonoid (Maybe a) where
131134 swaggerMempty = Nothing
132135 swaggerMappend x Nothing = x
133136 swaggerMappend _ y = y
137+
138+encodePretty :: ToJSON a => a -> BSL.ByteString
139+encodePretty = P.encodePretty' $ P.defConfig { P.confCompare = P.compare }
src/Data/OpenApi/Lens.hsnew file+166
@@ -0,0 +1,166 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE DataKinds #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+{-# OPTIONS_GHC -fno-warn-orphans #-}
12+-- |
13+-- Module: Data.OpenApi.Lens
14+-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
15+-- Stability: experimental
16+--
17+-- Lenses and prisms for Swagger.
18+module Data.OpenApi.Lens where
19+
20+import Control.Lens
21+import Data.Aeson (Value)
22+import Data.Scientific (Scientific)
23+import Data.OpenApi.Internal
24+import Data.OpenApi.Internal.Utils
25+import Data.Text (Text)
26+
27+-- * Classy lenses
28+
29+makeFields ''OpenApi
30+makeFields ''Components
31+makeFields ''Server
32+makeFields ''RequestBody
33+makeFields ''MediaTypeObject
34+makeFields ''Info
35+makeFields ''Contact
36+makeFields ''License
37+makeLensesWith swaggerFieldRules ''PathItem
38+makeFields ''Tag
39+makeFields ''Operation
40+makeLensesWith swaggerFieldRules ''Param
41+makeFields ''Header
42+makeLensesWith swaggerFieldRules ''Schema
43+makeFields ''NamedSchema
44+makeFields ''Xml
45+makeLensesWith swaggerFieldRules ''Responses
46+makeFields ''Response
47+makeLensesWith swaggerFieldRules ''SecurityScheme
48+makeFields ''ApiKeyParams
49+makeFields ''OAuth2ImplicitFlow
50+makeFields ''OAuth2PasswordFlow
51+makeFields ''OAuth2ClientCredentialsFlow
52+makeFields ''OAuth2AuthorizationCodeFlow
53+makeFields ''OAuth2Flow
54+makeFields ''OAuth2Flows
55+makeFields ''ExternalDocs
56+makeFields ''Encoding
57+makeFields ''Example
58+makeFields ''Discriminator
59+makeFields ''Link
60+
61+-- * Prisms
62+-- ** 'SecuritySchemeType' prisms
63+makePrisms ''SecuritySchemeType
64+-- ** 'Referenced' prisms
65+makePrisms ''Referenced
66+
67+-- ** 'OpenApiItems' prisms
68+
69+_OpenApiItemsArray :: Review OpenApiItems [Referenced Schema]
70+_OpenApiItemsArray
71+ = unto (\x -> OpenApiItemsArray x)
72+{- \x -> case x of
73+ OpenApiItemsPrimitive c p -> Left (OpenApiItemsPrimitive c p)
74+ OpenApiItemsObject o -> Left (OpenApiItemsObject o)
75+ OpenApiItemsArray a -> Right a
76+-}
77+
78+_OpenApiItemsObject :: Review OpenApiItems (Referenced Schema)
79+_OpenApiItemsObject
80+ = unto (\x -> OpenApiItemsObject x)
81+{- \x -> case x of
82+ OpenApiItemsPrimitive c p -> Left (OpenApiItemsPrimitive c p)
83+ OpenApiItemsObject o -> Right o
84+ OpenApiItemsArray a -> Left (OpenApiItemsArray a)
85+-}
86+
87+-- =============================================================
88+-- More helpful instances for easier access to schema properties
89+
90+type instance Index Responses = HttpStatusCode
91+type instance Index Operation = HttpStatusCode
92+
93+type instance IxValue Responses = Referenced Response
94+type instance IxValue Operation = Referenced Response
95+
96+instance Ixed Responses where ix n = responses . ix n
97+instance At Responses where at n = responses . at n
98+
99+instance Ixed Operation where ix n = responses . ix n
100+instance At Operation where at n = responses . at n
101+
102+instance HasType NamedSchema (Maybe OpenApiType) where type_ = schema.type_
103+
104+-- OVERLAPPABLE instances
105+
106+instance
107+ {-# OVERLAPPABLE #-}
108+ HasSchema s Schema
109+ => HasFormat s (Maybe Format) where
110+ format = schema.format
111+
112+instance
113+ {-# OVERLAPPABLE #-}
114+ HasSchema s Schema
115+ => HasItems s (Maybe OpenApiItems) where
116+ items = schema.items
117+
118+instance
119+ {-# OVERLAPPABLE #-}
120+ HasSchema s Schema
121+ => HasMaximum s (Maybe Scientific) where
122+ maximum_ = schema.maximum_
123+
124+instance {-# OVERLAPPABLE #-} HasSchema s Schema
125+ => HasExclusiveMaximum s (Maybe Bool) where
126+ exclusiveMaximum = schema.exclusiveMaximum
127+
128+instance {-# OVERLAPPABLE #-} HasSchema s Schema
129+ => HasMinimum s (Maybe Scientific) where
130+ minimum_ = schema.minimum_
131+
132+instance {-# OVERLAPPABLE #-} HasSchema s Schema
133+ => HasExclusiveMinimum s (Maybe Bool) where
134+ exclusiveMinimum = schema.exclusiveMinimum
135+
136+instance {-# OVERLAPPABLE #-} HasSchema s Schema
137+ => HasMaxLength s (Maybe Integer) where
138+ maxLength = schema.maxLength
139+
140+instance {-# OVERLAPPABLE #-} HasSchema s Schema
141+ => HasMinLength s (Maybe Integer) where
142+ minLength = schema.minLength
143+
144+instance {-# OVERLAPPABLE #-} HasSchema s Schema
145+ => HasPattern s (Maybe Text) where
146+ pattern = schema.pattern
147+
148+instance {-# OVERLAPPABLE #-} HasSchema s Schema
149+ => HasMaxItems s (Maybe Integer) where
150+ maxItems = schema.maxItems
151+
152+instance {-# OVERLAPPABLE #-} HasSchema s Schema
153+ => HasMinItems s (Maybe Integer) where
154+ minItems = schema.minItems
155+
156+instance {-# OVERLAPPABLE #-} HasSchema s Schema
157+ => HasUniqueItems s (Maybe Bool) where
158+ uniqueItems = schema.uniqueItems
159+
160+instance {-# OVERLAPPABLE #-} HasSchema s Schema
161+ => HasEnum s (Maybe [Value]) where
162+ enum_ = schema.enum_
163+
164+instance {-# OVERLAPPABLE #-} HasSchema s Schema
165+ => HasMultipleOf s (Maybe Scientific) where
166+ multipleOf = schema.multipleOf
src/Data/OpenApi/Operation.hsnew file+305
@@ -0,0 +1,305 @@
1+{-# LANGUAGE RankNTypes #-}
2+-- |
3+-- Module: Data.OpenApi.Operation
4+-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
5+-- Stability: experimental
6+--
7+-- Helper traversals and functions for Swagger operations manipulations.
8+-- These might be useful when you already have Swagger specification
9+-- generated by something else.
10+module Data.OpenApi.Operation (
11+ -- * Operation traversals
12+ allOperations,
13+ operationsOf,
14+
15+ -- * Manipulation
16+ -- ** Tags
17+ applyTags,
18+ applyTagsFor,
19+
20+ -- ** Responses
21+ setResponse,
22+ setResponseWith,
23+ setResponseFor,
24+ setResponseForWith,
25+
26+ -- ** Paths
27+ prependPath,
28+
29+ -- * Miscellaneous
30+ declareResponse,
31+) where
32+
33+import Prelude ()
34+import Prelude.Compat
35+
36+import Control.Lens
37+import Data.Data.Lens
38+import Data.List.Compat
39+import Data.Maybe (mapMaybe)
40+import Data.Proxy
41+import qualified Data.Set as Set
42+import Data.Text (Text)
43+import Network.HTTP.Media (MediaType)
44+
45+import Data.OpenApi.Declare
46+import Data.OpenApi.Internal
47+import Data.OpenApi.Lens
48+import Data.OpenApi.Schema
49+
50+import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
51+import qualified Data.HashSet.InsOrd as InsOrdHS
52+
53+-- $setup
54+-- >>> import Data.Aeson
55+-- >>> import Data.Proxy
56+-- >>> import Data.Time
57+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
58+-- >>> import Data.OpenApi.Internal.Utils
59+
60+-- | Prepend path piece to all operations of the spec.
61+-- Leading and trailing slashes are trimmed/added automatically.
62+--
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
69+prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
70+ where
71+ x </> y = case trim y of
72+ "" -> "/" <> trim x
73+ y' -> "/" <> trim x <> "/" <> y'
74+
75+ trim = dropWhile (== '/') . dropWhileEnd (== '/')
76+
77+-- | All operations of a Swagger spec.
78+allOperations :: Traversal' OpenApi Operation
79+allOperations = paths.traverse.template
80+
81+-- | @'operationsOf' sub@ will traverse only those operations
82+-- that are present in @sub@. Note that @'Operation'@ is determined
83+-- by both path and method.
84+--
85+-- >>> 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
146+operationsOf sub = paths.itraversed.withIndex.subops
147+ where
148+ -- | Traverse operations that correspond to paths and methods of the sub API.
149+ subops :: Traversal' (FilePath, PathItem) Operation
150+ subops f (path, item) = case InsOrdHashMap.lookup path (sub ^. paths) of
151+ Just subitem -> (,) path <$> methodsOf subitem f item
152+ Nothing -> pure (path, item)
153+
154+ -- | Traverse operations that exist in a given @'PathItem'@
155+ -- This is used to traverse only the operations that exist in sub API.
156+ methodsOf :: PathItem -> Traversal' PathItem Operation
157+ methodsOf pathItem = partsOf template . itraversed . indices (`elem` ns) . _Just
158+ where
159+ ops = pathItem ^.. template :: [Maybe Operation]
160+ ns = mapMaybe (fmap fst . sequenceA) $ zip [0..] ops
161+
162+-- | Apply tags to all operations and update the global list of tags.
163+--
164+-- @
165+-- 'applyTags' = 'applyTagsFor' 'allOperations'
166+-- @
167+applyTags :: [Tag] -> OpenApi -> OpenApi
168+applyTags = applyTagsFor allOperations
169+
170+-- | Apply tags to a part of Swagger spec and update the global
171+-- list of tags.
172+applyTagsFor :: Traversal' OpenApi Operation -> [Tag] -> OpenApi -> OpenApi
173+applyTagsFor ops ts swag = swag
174+ & ops . tags %~ (<> InsOrdHS.fromList (map _tagName ts))
175+ & tags %~ (<> InsOrdHS.fromList ts)
176+
177+-- | Construct a response with @'Schema'@ while declaring all
178+-- necessary schema definitions.
179+--
180+-- FIXME doc
181+--
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
203+declareResponse cType proxy = do
204+ s <- declareSchemaRef proxy
205+ return (mempty & content.at cType ?~ (mempty & schema ?~ s))
206+
207+-- | Set response for all operations.
208+-- This will also update global schema definitions.
209+--
210+-- If the response already exists it will be overwritten.
211+--
212+-- @
213+-- 'setResponse' = 'setResponseFor' 'allOperations'
214+-- @
215+--
216+-- Example:
217+--
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+-- }
255+--
256+-- See also @'setResponseWith'@.
257+setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
258+setResponse = setResponseFor allOperations
259+
260+-- | Set or update response for all operations.
261+-- This will also update global schema definitions.
262+--
263+-- If the response already exists, but it can't be dereferenced (invalid @\$ref@),
264+-- then just the new response is used.
265+--
266+-- @
267+-- 'setResponseWith' = 'setResponseForWith' 'allOperations'
268+-- @
269+--
270+-- See also @'setResponse'@.
271+setResponseWith :: (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
272+setResponseWith = setResponseForWith allOperations
273+
274+-- | Set response for specified operations.
275+-- This will also update global schema definitions.
276+--
277+-- If the response already exists it will be overwritten.
278+--
279+-- See also @'setResponseForWith'@.
280+setResponseFor :: Traversal' OpenApi Operation -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
281+setResponseFor ops code dres swag = swag
282+ & components.schemas %~ (<> defs)
283+ & ops . at code ?~ Inline res
284+ where
285+ (defs, res) = runDeclare dres mempty
286+
287+-- | Set or update response for specified operations.
288+-- This will also update global schema definitions.
289+--
290+-- If the response already exists, but it can't be dereferenced (invalid @\$ref@),
291+-- then just the new response is used.
292+--
293+-- See also @'setResponseFor'@.
294+setResponseForWith :: Traversal' OpenApi Operation -> (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
295+setResponseForWith ops f code dres swag = swag
296+ & components.schemas %~ (<> defs)
297+ & ops . at code %~ Just . Inline . combine
298+ where
299+ (defs, new) = runDeclare dres mempty
300+
301+ combine (Just (Ref (Reference n))) = case swag ^. components.responses.at n of
302+ Just old -> f old new
303+ Nothing -> new -- response name can't be dereferenced, replacing with new response
304+ combine (Just (Inline old)) = f old new
305+ combine Nothing = new
src/Data/OpenApi/Optics.hsnew file+328
@@ -0,0 +1,328 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE OverloadedLabels #-}
5+{-# LANGUAGE TemplateHaskell #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE UndecidableInstances #-}
8+{-# OPTIONS_GHC -fno-warn-orphans #-}
9+-- |
10+-- Module: Data.OpenApi.Optics
11+-- Maintainer: Andrzej Rybczak <andrzej@rybczak.net>
12+-- Stability: experimental
13+--
14+-- Lenses and prisms for the <https://hackage.haskell.org/package/optics optics>
15+-- library.
16+--
17+-- >>> import Data.Aeson
18+-- >>> import Optics.Core
19+-- >>> :set -XOverloadedLabels
20+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
21+--
22+-- Example from the "Data.OpenApi" module using @optics@:
23+--
24+-- >>> :{
25+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
26+-- & #components % #schemas .~ [ ("User", mempty & #type ?~ OpenApiString) ]
27+-- & #paths .~
28+-- [ ("/user", mempty & #get ?~ (mempty
29+-- & at 200 ?~ ("OK" & #_Inline % #content % at "application/json" ?~ (mempty & #schema ?~ Ref (Reference "User")))
30+-- & at 404 ?~ "User info not found")) ]
31+-- :}
32+-- {
33+-- "components": {
34+-- "schemas": {
35+-- "User": {
36+-- "type": "string"
37+-- }
38+-- }
39+-- },
40+-- "info": {
41+-- "title": "",
42+-- "version": ""
43+-- },
44+-- "openapi": "3.0.0",
45+-- "paths": {
46+-- "/user": {
47+-- "get": {
48+-- "responses": {
49+-- "200": {
50+-- "content": {
51+-- "application/json": {
52+-- "schema": {
53+-- "$ref": "#/components/schemas/User"
54+-- }
55+-- }
56+-- },
57+-- "description": "OK"
58+-- },
59+-- "404": {
60+-- "description": "User info not found"
61+-- }
62+-- }
63+-- }
64+-- }
65+-- }
66+-- }
67+--
68+-- For convenience optics are defined as /labels/. It means that field accessor
69+-- names can be overloaded for different types. One such common field is
70+-- @#description@. Many components of a Swagger specification can have
71+-- descriptions, and you can use the same name for them:
72+--
73+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & #description .~ "No content"
74+-- {
75+-- "description": "No content"
76+-- }
77+-- >>> :{
78+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
79+-- & #type ?~ OpenApiBoolean
80+-- & #description ?~ "To be or not to be"
81+-- :}
82+-- {
83+-- "description": "To be or not to be",
84+-- "type": "boolean"
85+-- }
86+--
87+-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and
88+-- @'Responses'@ have direct access to it via @'Optics.Core.At.at'@. Example:
89+--
90+-- >>> :{
91+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
92+-- & at 404 ?~ "Not found"
93+-- :}
94+-- {
95+-- "responses": {
96+-- "404": {
97+-- "description": "Not found"
98+-- }
99+-- }
100+-- }
101+module Data.OpenApi.Optics () where
102+
103+import Data.Aeson (Value)
104+import Data.Scientific (Scientific)
105+import Data.OpenApi.Internal
106+import Data.OpenApi.Internal.Utils
107+import Data.Text (Text)
108+import Optics.Core
109+import Optics.TH
110+
111+-- Lenses
112+
113+makeFieldLabels ''OpenApi
114+makeFieldLabels ''Components
115+makeFieldLabels ''Server
116+makeFieldLabels ''ServerVariable
117+makeFieldLabels ''RequestBody
118+makeFieldLabels ''MediaTypeObject
119+makeFieldLabels ''Info
120+makeFieldLabels ''Contact
121+makeFieldLabels ''License
122+makeFieldLabels ''PathItem
123+makeFieldLabels ''Tag
124+makeFieldLabels ''Operation
125+makeFieldLabels ''Param
126+makeFieldLabels ''Header
127+makeFieldLabels ''Schema
128+makeFieldLabels ''NamedSchema
129+makeFieldLabels ''Xml
130+makeFieldLabels ''Responses
131+makeFieldLabels ''Response
132+makeFieldLabels ''SecurityScheme
133+makeFieldLabels ''ApiKeyParams
134+makeFieldLabels ''OAuth2ImplicitFlow
135+makeFieldLabels ''OAuth2PasswordFlow
136+makeFieldLabels ''OAuth2ClientCredentialsFlow
137+makeFieldLabels ''OAuth2AuthorizationCodeFlow
138+makeFieldLabels ''OAuth2Flow
139+makeFieldLabels ''OAuth2Flows
140+makeFieldLabels ''ExternalDocs
141+makeFieldLabels ''Encoding
142+makeFieldLabels ''Example
143+makeFieldLabels ''Discriminator
144+makeFieldLabels ''Link
145+
146+-- Prisms
147+
148+makePrismLabels ''SecuritySchemeType
149+makePrismLabels ''Referenced
150+
151+-- OpenApiItems prisms
152+
153+instance
154+ ( a ~ [Referenced Schema]
155+ , b ~ [Referenced Schema]
156+ ) => LabelOptic "_OpenApiItemsArray"
157+ A_Review
158+ OpenApiItems
159+ OpenApiItems
160+ a
161+ b where
162+ labelOptic = unto (\x -> OpenApiItemsArray x)
163+ {-# INLINE labelOptic #-}
164+
165+instance
166+ ( a ~ Referenced Schema
167+ , b ~ Referenced Schema
168+ ) => LabelOptic "_OpenApiItemsObject"
169+ A_Review
170+ OpenApiItems
171+ OpenApiItems
172+ a
173+ b where
174+ labelOptic = unto (\x -> OpenApiItemsObject x)
175+ {-# INLINE labelOptic #-}
176+
177+-- =============================================================
178+-- More helpful instances for easier access to schema properties
179+
180+type instance Index Responses = HttpStatusCode
181+type instance Index Operation = HttpStatusCode
182+
183+type instance IxValue Responses = Referenced Response
184+type instance IxValue Operation = Referenced Response
185+
186+instance Ixed Responses where
187+ ix n = #responses % ix n
188+ {-# INLINE ix #-}
189+instance At Responses where
190+ at n = #responses % at n
191+ {-# INLINE at #-}
192+
193+instance Ixed Operation where
194+ ix n = #responses % ix n
195+ {-# INLINE ix #-}
196+instance At Operation where
197+ at n = #responses % at n
198+ {-# INLINE at #-}
199+
200+-- #type
201+
202+instance
203+ ( a ~ Maybe OpenApiType
204+ , b ~ Maybe OpenApiType
205+ ) => LabelOptic "type" A_Lens NamedSchema NamedSchema a b where
206+ labelOptic = #schema % #type
207+ {-# INLINE labelOptic #-}
208+
209+-- #default
210+
211+instance
212+ ( a ~ Maybe Value, b ~ Maybe Value
213+ ) => LabelOptic "default" A_Lens NamedSchema NamedSchema a b where
214+ labelOptic = #schema % #default
215+ {-# INLINE labelOptic #-}
216+
217+-- #format
218+
219+instance
220+ ( a ~ Maybe Format, b ~ Maybe Format
221+ ) => LabelOptic "format" A_Lens NamedSchema NamedSchema a b where
222+ labelOptic = #schema % #format
223+ {-# INLINE labelOptic #-}
224+
225+-- #items
226+
227+instance
228+ ( a ~ Maybe OpenApiItems
229+ , b ~ Maybe OpenApiItems
230+ ) => LabelOptic "items" A_Lens NamedSchema NamedSchema a b where
231+ labelOptic = #schema % #items
232+ {-# INLINE labelOptic #-}
233+
234+-- #maximum
235+
236+instance
237+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
238+ ) => LabelOptic "maximum" A_Lens NamedSchema NamedSchema a b where
239+ labelOptic = #schema % #maximum
240+ {-# INLINE labelOptic #-}
241+
242+-- #exclusiveMaximum
243+
244+instance
245+ ( a ~ Maybe Bool, b ~ Maybe Bool
246+ ) => LabelOptic "exclusiveMaximum" A_Lens NamedSchema NamedSchema a b where
247+ labelOptic = #schema % #exclusiveMaximum
248+ {-# INLINE labelOptic #-}
249+
250+-- #minimum
251+
252+instance
253+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
254+ ) => LabelOptic "minimum" A_Lens NamedSchema NamedSchema a b where
255+ labelOptic = #schema % #minimum
256+ {-# INLINE labelOptic #-}
257+
258+-- #exclusiveMinimum
259+
260+instance
261+ ( a ~ Maybe Bool, b ~ Maybe Bool
262+ ) => LabelOptic "exclusiveMinimum" A_Lens NamedSchema NamedSchema a b where
263+ labelOptic = #schema % #exclusiveMinimum
264+ {-# INLINE labelOptic #-}
265+
266+-- #maxLength
267+
268+instance
269+ ( a ~ Maybe Integer, b ~ Maybe Integer
270+ ) => LabelOptic "maxLength" A_Lens NamedSchema NamedSchema a b where
271+ labelOptic = #schema % #maxLength
272+ {-# INLINE labelOptic #-}
273+
274+-- #minLength
275+
276+instance
277+ ( a ~ Maybe Integer, b ~ Maybe Integer
278+ ) => LabelOptic "minLength" A_Lens NamedSchema NamedSchema a b where
279+ labelOptic = #schema % #minLength
280+ {-# INLINE labelOptic #-}
281+
282+-- #pattern
283+
284+instance
285+ ( a ~ Maybe Text, b ~ Maybe Text
286+ ) => LabelOptic "pattern" A_Lens NamedSchema NamedSchema a b where
287+ labelOptic = #schema % #pattern
288+ {-# INLINE labelOptic #-}
289+
290+-- #maxItems
291+
292+instance
293+ ( a ~ Maybe Integer, b ~ Maybe Integer
294+ ) => LabelOptic "maxItems" A_Lens NamedSchema NamedSchema a b where
295+ labelOptic = #schema % #maxItems
296+ {-# INLINE labelOptic #-}
297+
298+-- #minItems
299+
300+instance
301+ ( a ~ Maybe Integer, b ~ Maybe Integer
302+ ) => LabelOptic "minItems" A_Lens NamedSchema NamedSchema a b where
303+ labelOptic = #schema % #minItems
304+ {-# INLINE labelOptic #-}
305+
306+-- #uniqueItems
307+
308+instance
309+ ( a ~ Maybe Bool, b ~ Maybe Bool
310+ ) => LabelOptic "uniqueItems" A_Lens NamedSchema NamedSchema a b where
311+ labelOptic = #schema % #uniqueItems
312+ {-# INLINE labelOptic #-}
313+
314+-- #enum
315+
316+instance
317+ ( a ~ Maybe [Value], b ~ Maybe [Value]
318+ ) => LabelOptic "enum" A_Lens NamedSchema NamedSchema a b where
319+ labelOptic = #schema % #enum
320+ {-# INLINE labelOptic #-}
321+
322+-- #multipleOf
323+
324+instance
325+ ( a ~ Maybe Scientific, b ~ Maybe Scientific
326+ ) => LabelOptic "multipleOf" A_Lens NamedSchema NamedSchema a b where
327+ labelOptic = #schema % #multipleOf
328+ {-# INLINE labelOptic #-}
src/Data/Swagger/ParamSchema.hs → src/Data/OpenApi/ParamSchema.hsrenamed+7−7
@@ -1,10 +1,10 @@
11-- |
2−-- Module: Data.Swagger.ParamSchema
2+-- Module: Data.OpenApi.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.Swagger.ParamSchema (
7+module Data.OpenApi.ParamSchema (
88 -- * Encoding
99 ToParamSchema(..),
1010
@@ -13,14 +13,14 @@ module Data.Swagger.ParamSchema (
1313 toParamSchemaBoundedIntegral,
1414
1515 -- * Schema templates
16− passwordParamSchema,
17− binaryParamSchema,
18− byteParamSchema,
16+ passwordSchema,
17+ binarySchema,
18+ byteSchema,
1919
2020 -- * Generic encoding configuration
2121 SchemaOptions(..),
2222 defaultSchemaOptions,
2323) where
2424
25−import Data.Swagger.Internal.ParamSchema
26−import Data.Swagger.SchemaOptions
25+import Data.OpenApi.Internal.ParamSchema
26+import Data.OpenApi.SchemaOptions
src/Data/Swagger/Schema.hs → src/Data/OpenApi/Schema.hsrenamed+4−13
@@ -1,10 +1,10 @@
11-- |
2−-- Module: Data.Swagger.Schema
2+-- Module: Data.OpenApi.Schema
33-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
44-- Stability: experimental
55--
66-- Types and functions for working with Swagger schema.
7−module Data.Swagger.Schema (
7+module Data.OpenApi.Schema (
88 -- * Encoding
99 ToSchema(..),
1010 declareSchema,
@@ -32,15 +32,6 @@ module Data.Swagger.Schema (
3232 paramSchemaToNamedSchema,
3333 paramSchemaToSchema,
3434
35− -- ** Unrestricted versions
36− genericDeclareNamedSchemaUnrestricted,
37− genericDeclareSchemaUnrestricted,
38−
39− -- * Schema templates
40− passwordSchema,
41− binarySchema,
42− byteSchema,
43−
4435 -- * Sketching @'Schema'@s using @'ToJSON'@
4536 sketchSchema,
4637 sketchStrictSchema,
@@ -57,5 +48,5 @@ module Data.Swagger.Schema (
5748 fromAesonOptions,
5849) where
5950
60−import Data.Swagger.Internal.Schema
61−import Data.Swagger.SchemaOptions
51+import Data.OpenApi.Internal.Schema
52+import Data.OpenApi.SchemaOptions
src/Data/Swagger/Schema/Generator.hs → src/Data/OpenApi/Schema/Generator.hsrenamed+37−26
@@ -2,53 +2,64 @@
22{-# LANGUAGE OverloadedLists #-}
33{-# LANGUAGE ScopedTypeVariables #-}
44
5−module Data.Swagger.Schema.Generator where
5+module Data.OpenApi.Schema.Generator where
66
7−import Prelude ()
7+import Prelude ()
88import Prelude.Compat
99
1010import Control.Lens.Operators
11−import Control.Monad (filterM)
11+import Control.Monad (filterM)
1212import Data.Aeson
1313import Data.Aeson.Types
14−import qualified Data.HashMap.Strict.InsOrd as M
15−import Data.Maybe
14+import qualified Data.HashMap.Strict.InsOrd as M
1615import Data.Maybe
1716import Data.Proxy
1817import Data.Scientific
19−import qualified Data.Set as S
20−import Data.Swagger
21−import Data.Swagger.Declare
22−import qualified Data.Text as T
23−import qualified Data.Vector as V
24−import Test.QuickCheck (arbitrary)
18+import qualified Data.Set as S
19+import Data.OpenApi
20+import Data.OpenApi.Declare
21+import Data.OpenApi.Internal.Schema.Validation (inferSchemaTypes)
22+import qualified Data.Text as T
23+import qualified Data.Vector as V
24+import Test.QuickCheck (arbitrary)
2525import Test.QuickCheck.Gen
2626import Test.QuickCheck.Property
2727
28+import Data.OpenApi.Aeson.Compat (fromInsOrdHashMap)
29+
30+-- | Note: 'schemaGen' may 'error', if schema type is not specified,
31+-- and cannot be inferred.
2832schemaGen :: Definitions Schema -> Schema -> Gen Value
2933schemaGen _ schema
30− | Just cases <- schema ^. paramSchema . enum_ = elements cases
34+ | Just cases <- schema ^. enum_ = elements cases
35+schemaGen defns schema
36+ | Just variants <- schema ^. oneOf = schemaGen defns =<< elements (dereference defns <$> variants)
3137schemaGen defns schema =
3238 case schema ^. type_ of
33− SwaggerBoolean -> Bool <$> elements [True, False]
34− SwaggerNull -> pure Null
35− SwaggerNumber
39+ Nothing ->
40+ case inferSchemaTypes schema of
41+ [ inferredType ] -> schemaGen defns (schema & type_ ?~ inferredType)
42+ -- Gen is not MonadFail
43+ _ -> error "unable to infer schema type"
44+ Just OpenApiBoolean -> Bool <$> elements [True, False]
45+ Just OpenApiNull -> pure Null
46+ Just OpenApiNumber
3647 | Just min <- schema ^. minimum_
3748 , Just max <- schema ^. maximum_ ->
3849 Number . fromFloatDigits <$>
3950 choose (toRealFloat min, toRealFloat max :: Double)
4051 | otherwise -> Number .fromFloatDigits <$> (arbitrary :: Gen Double)
41− SwaggerInteger
52+ Just OpenApiInteger
4253 | Just min <- schema ^. minimum_
4354 , Just max <- schema ^. maximum_ ->
4455 Number . fromInteger <$>
4556 choose (truncate min, truncate max)
4657 | otherwise -> Number . fromInteger <$> arbitrary
47− SwaggerArray
58+ Just OpenApiArray
4859 | Just 0 <- schema ^. maxLength -> pure $ Array V.empty
4960 | Just items <- schema ^. items ->
5061 case items of
51− SwaggerItemsObject ref -> do
62+ OpenApiItemsObject ref -> do
5263 size <- getSize
5364 let itemSchema = dereference defns ref
5465 minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minItems
@@ -56,17 +67,17 @@ schemaGen defns schema =
5667 arrayLength <- choose (minLength', max minLength' maxLength')
5768 generatedArray <- vectorOf arrayLength $ schemaGen defns itemSchema
5869 return . Array $ V.fromList generatedArray
59− SwaggerItemsArray refs ->
70+ OpenApiItemsArray refs ->
6071 let itemGens = schemaGen defns . dereference defns <$> refs
6172 in fmap (Array . V.fromList) $ sequence itemGens
62− SwaggerString -> do
73+ Just OpenApiString -> do
6374 size <- getSize
6475 let minLength' = fromMaybe 0 $ fromInteger <$> schema ^. minLength
6576 let maxLength' = fromMaybe size $ fromInteger <$> schema ^. maxLength
6677 length <- choose (minLength', max minLength' maxLength')
6778 str <- vectorOf length arbitrary
6879 return . String $ T.pack str
69− SwaggerObject -> do
80+ Just OpenApiObject -> do
7081 size <- getSize
7182 let props = dereference defns <$> schema ^. properties
7283 reqKeys = S.fromList $ schema ^. required
@@ -86,11 +97,11 @@ schemaGen defns schema =
8697 return . M.fromList $ zip additionalKeys (repeat . schemaGen defns $ dereference defns addlSchema)
8798 _ -> return []
8899 x <- sequence $ gens <> additionalGens
89− return . Object $ M.toHashMap x
90− where
91− dereference :: Definitions a -> Referenced a -> a
92− dereference _ (Inline a) = a
93− dereference defs (Ref (Reference ref)) = fromJust $ M.lookup ref defs
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
94105
95106genValue :: (ToSchema a) => Proxy a -> Gen Value
96107genValue p =
src/Data/Swagger/Schema/Validation.hs → src/Data/OpenApi/Schema/Validation.hsrenamed+9−7
@@ -1,10 +1,10 @@
11-- |
2−-- Module: Data.Swagger.Schema.Validation
2+-- Module: Data.OpenApi.Schema.Validation
33-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
44-- Stability: experimental
55--
66-- Validate JSON values with Swagger Schema.
7−module Data.Swagger.Schema.Validation (
7+module Data.OpenApi.Schema.Validation (
88 -- * How to use validation
99 -- $howto
1010
@@ -19,21 +19,23 @@ module Data.Swagger.Schema.Validation (
1919 ValidationError,
2020
2121 -- ** Using 'ToJSON' and 'ToSchema'
22+ validatePrettyToJSON,
2223 validateToJSON,
2324 validateToJSONWithPatternChecker,
25+ renderValidationErrors,
2426
2527 -- ** Using 'Value' and 'Schema'
2628 validateJSON,
2729 validateJSONWithPatternChecker,
2830) where
2931
30−import Data.Swagger.Internal.Schema.Validation
32+import Data.OpenApi.Internal.Schema.Validation
3133
3234-- $setup
3335-- >>> import Control.Lens
3436-- >>> import Data.Aeson
3537-- >>> import Data.Proxy
36−-- >>> import Data.Swagger
38+-- >>> import Data.OpenApi
3739-- >>> import GHC.Generics
3840-- >>> :set -XDeriveGeneric
3941
@@ -69,11 +71,11 @@ import Data.Swagger.Internal.Schema.Validation
6971-- generally fails for @null@ JSON:
7072--
7173-- >>> validateToJSON (Nothing :: Maybe String)
72−-- ["expected JSON value of type SwaggerString"]
74+-- ["expected JSON value of type OpenApiString"]
7375-- >>> validateToJSON ([Just "hello", Nothing] :: [Maybe String])
74−-- ["expected JSON value of type SwaggerString"]
76+-- ["expected JSON value of type OpenApiString"]
7577-- >>> validateToJSON (123, Nothing :: Maybe String)
76−-- ["expected JSON value of type SwaggerString"]
78+-- ["expected JSON value of type OpenApiString"]
7779--
7880-- However, when @'Maybe' a@ is a type of a record field,
7981-- validation takes @'required'@ property of the @'Schema'@
src/Data/Swagger/SchemaOptions.hs → src/Data/OpenApi/SchemaOptions.hsrenamed+7−3
@@ -1,11 +1,11 @@
11{-# LANGUAGE RecordWildCards #-}
22-- |
3−-- Module: Data.Swagger.SchemaOptions
3+-- Module: Data.OpenApi.SchemaOptions
44-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
55-- Stability: experimental
66--
77-- Generic deriving options for @'ToParamSchema'@ and @'ToSchema'@.
8−module Data.Swagger.SchemaOptions where
8+module Data.OpenApi.SchemaOptions where
99
1010import qualified Data.Aeson.Types as Aeson
1111
@@ -22,6 +22,8 @@ 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
2527 }
2628
2729-- | Default encoding @'SchemaOptions'@.
@@ -33,6 +35,7 @@ data SchemaOptions = SchemaOptions
3335-- , 'datatypeNameModifier' = id
3436-- , 'allNullaryToStringTag' = True
3537-- , 'unwrapUnaryRecords' = False
38+-- , 'sumEncoding' = 'Aeson.defaultTaggedObject'
3639-- }
3740-- @
3841defaultSchemaOptions :: SchemaOptions
@@ -42,6 +45,7 @@ defaultSchemaOptions = SchemaOptions
4245 , datatypeNameModifier = id
4346 , allNullaryToStringTag = True
4447 , unwrapUnaryRecords = False
48+ , sumEncoding = Aeson.defaultTaggedObject
4549 }
4650
4751-- | Convert 'Aeson.Options' to 'SchemaOptions'.
@@ -56,7 +60,6 @@ defaultSchemaOptions = SchemaOptions
5660-- Note that these fields have no effect on `SchemaOptions`:
5761--
5862-- * 'Aeson.omitNothingFields'
59−-- * 'Aeson.sumEncoding'
6063-- * 'Aeson.tagSingleConstructors'
6164--
6265-- The rest is defined as in 'defaultSchemaOptions'.
@@ -69,4 +72,5 @@ fromAesonOptions opts = defaultSchemaOptions
6972 , constructorTagModifier = Aeson.constructorTagModifier opts
7073 , allNullaryToStringTag = Aeson.allNullaryToStringTag opts
7174 , unwrapUnaryRecords = Aeson.unwrapUnaryRecords opts
75+ , sumEncoding = Aeson.sumEncoding opts
7276 }
src/Data/Swagger/Internal.hsdeleted−1386
@@ -1,1386 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DeriveDataTypeable #-}
4−{-# LANGUAGE DeriveFunctor #-}
5−{-# LANGUAGE DeriveGeneric #-}
6−{-# LANGUAGE DeriveTraversable #-}
7−{-# LANGUAGE FlexibleContexts #-}
8−{-# LANGUAGE FlexibleInstances #-}
9−{-# LANGUAGE GADTs #-}
10−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11−{-# LANGUAGE OverloadedStrings #-}
12−{-# LANGUAGE RecordWildCards #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE StandaloneDeriving #-}
15−{-# LANGUAGE TemplateHaskell #-}
16−{-# LANGUAGE TypeFamilies #-}
17−{-# LANGUAGE UndecidableInstances #-}
18−#if __GLASGOW_HASKELL__ <710
19−{-# LANGUAGE PolyKinds #-}
20−#endif
21−#include "overlapping-compat.h"
22−module Data.Swagger.Internal where
23−
24−import Prelude ()
25−import Prelude.Compat
26−
27−import Control.Lens ((&), (.~), (?~))
28−import Control.Applicative
29−import Data.Aeson
30−import qualified Data.Aeson.Types as JSON
31−import Data.Data (Data(..), Typeable, mkConstr, mkDataType, Fixity(..), Constr, DataType, constrIndex)
32−import qualified Data.HashMap.Strict as HashMap
33−import Data.Map (Map)
34−import qualified Data.Map as Map
35−import Data.Monoid (Monoid (..))
36−import Data.Semigroup.Compat (Semigroup (..))
37−import Data.Scientific (Scientific)
38−import Data.Set (Set)
39−import Data.String (IsString(..))
40−import Data.Text (Text)
41−import qualified Data.Text as Text
42−import GHC.Generics (Generic)
43−import Network.Socket (HostName, PortNumber)
44−import Network.HTTP.Media (MediaType)
45−import Text.Read (readMaybe)
46−
47−import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
48−import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
49−
50−import Generics.SOP.TH (deriveGeneric)
51−import Data.Swagger.Internal.AesonUtils (sopSwaggerGenericToJSON
52− ,sopSwaggerGenericToJSONWithOpts
53− ,sopSwaggerGenericParseJSON
54− ,HasSwaggerAesonOptions(..)
55− ,AesonDefaultValue(..)
56− ,mkSwaggerAesonOptions
57− ,saoAdditionalPairs
58− ,saoSubObject)
59−import Data.Swagger.Internal.Utils
60−
61−#if MIN_VERSION_aeson(0,10,0)
62−import Data.Swagger.Internal.AesonUtils (sopSwaggerGenericToEncoding)
63−#define DEFINE_TOENCODING toEncoding = sopSwaggerGenericToEncoding
64−#else
65−#define DEFINE_TOENCODING
66−#endif
67−
68−-- $setup
69−-- >>> :seti -XDataKinds
70−-- >>> import Data.Aeson
71−
72−-- | A list of definitions that can be used in references.
73−type Definitions = InsOrdHashMap Text
74−
75−-- | This is the root document object for the API specification.
76−data Swagger = Swagger
77− { -- | Provides metadata about the API.
78− -- The metadata can be used by the clients if needed.
79− _swaggerInfo :: Info
80−
81− -- | The host (name or ip) serving the API. It MAY include a port.
82− -- If the host is not included, the host serving the documentation is to be used (including the port).
83− , _swaggerHost :: Maybe Host
84−
85− -- | The base path on which the API is served, which is relative to the host.
86− -- If it is not included, the API is served directly under the host.
87− -- The value MUST start with a leading slash (/).
88− , _swaggerBasePath :: Maybe FilePath
89−
90− -- | The transfer protocol of the API.
91− -- If the schemes is not included, the default scheme to be used is the one used to access the Swagger definition itself.
92− , _swaggerSchemes :: Maybe [Scheme]
93−
94− -- | A list of MIME types the APIs can consume.
95− -- This is global to all APIs but can be overridden on specific API calls.
96− , _swaggerConsumes :: MimeList
97−
98− -- | A list of MIME types the APIs can produce.
99− -- This is global to all APIs but can be overridden on specific API calls.
100− , _swaggerProduces :: MimeList
101−
102− -- | The available paths and operations for the API.
103− -- Holds the relative paths to the individual endpoints.
104− -- The path is appended to the @'basePath'@ in order to construct the full URL.
105− , _swaggerPaths :: InsOrdHashMap FilePath PathItem
106−
107− -- | An object to hold data types produced and consumed by operations.
108− , _swaggerDefinitions :: Definitions Schema
109−
110− -- | An object to hold parameters that can be used across operations.
111− -- This property does not define global parameters for all operations.
112− , _swaggerParameters :: Definitions Param
113−
114− -- | An object to hold responses that can be used across operations.
115− -- This property does not define global responses for all operations.
116− , _swaggerResponses :: Definitions Response
117−
118− -- | Security scheme definitions that can be used across the specification.
119− , _swaggerSecurityDefinitions :: Definitions SecurityScheme
120−
121− -- | A declaration of which security schemes are applied for the API as a whole.
122− -- The list of values describes alternative security schemes that can be used
123− -- (that is, there is a logical OR between the security requirements).
124− -- Individual operations can override this definition.
125− , _swaggerSecurity :: [SecurityRequirement]
126−
127− -- | A list of tags used by the specification with additional metadata.
128− -- The order of the tags can be used to reflect on their order by the parsing tools.
129− -- Not all tags that are used by the Operation Object must be declared.
130− -- The tags that are not declared may be organized randomly or based on the tools' logic.
131− -- Each tag name in the list MUST be unique.
132− , _swaggerTags :: Set Tag
133−
134− -- | Additional external documentation.
135− , _swaggerExternalDocs :: Maybe ExternalDocs
136− } deriving (Eq, Show, Generic, Data, Typeable)
137−
138−-- | The object provides metadata about the API.
139−-- The metadata can be used by the clients if needed,
140−-- and can be presented in the Swagger-UI for convenience.
141−data Info = Info
142− { -- | The title of the application.
143− _infoTitle :: Text
144−
145− -- | A short description of the application.
146− -- GFM syntax can be used for rich text representation.
147− , _infoDescription :: Maybe Text
148−
149− -- | The Terms of Service for the API.
150− , _infoTermsOfService :: Maybe Text
151−
152− -- | The contact information for the exposed API.
153− , _infoContact :: Maybe Contact
154−
155− -- | The license information for the exposed API.
156− , _infoLicense :: Maybe License
157−
158− -- | Provides the version of the application API
159− -- (not to be confused with the specification version).
160− , _infoVersion :: Text
161− } deriving (Eq, Show, Generic, Data, Typeable)
162−
163−-- | Contact information for the exposed API.
164−data Contact = Contact
165− { -- | The identifying name of the contact person/organization.
166− _contactName :: Maybe Text
167−
168− -- | The URL pointing to the contact information.
169− , _contactUrl :: Maybe URL
170−
171− -- | The email address of the contact person/organization.
172− , _contactEmail :: Maybe Text
173− } deriving (Eq, Show, Generic, Data, Typeable)
174−
175−-- | License information for the exposed API.
176−data License = License
177− { -- | The license name used for the API.
178− _licenseName :: Text
179−
180− -- | A URL to the license used for the API.
181− , _licenseUrl :: Maybe URL
182− } deriving (Eq, Show, Generic, Data, Typeable)
183−
184−instance IsString License where
185− fromString s = License (fromString s) Nothing
186−
187−-- | The host (name or ip) serving the API. It MAY include a port.
188−data Host = Host
189− { _hostName :: HostName -- ^ Host name.
190− , _hostPort :: Maybe PortNumber -- ^ Optional port.
191− } deriving (Eq, Show, Generic, Typeable)
192−
193−instance IsString Host where
194− fromString s = Host s Nothing
195−
196−hostConstr :: Constr
197−hostConstr = mkConstr hostDataType "Host" [] Prefix
198−
199−hostDataType :: DataType
200−hostDataType = mkDataType "Data.Swagger.Host" [hostConstr]
201−
202−instance Data Host where
203− gunfold k z c = case constrIndex c of
204− 1 -> k (k (z (\name mport -> Host name (fromInteger <$> mport))))
205− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type Host."
206− toConstr (Host _ _) = hostConstr
207− dataTypeOf _ = hostDataType
208−
209−-- | The transfer protocol of the API.
210−data Scheme
211− = Http
212− | Https
213− | Ws
214− | Wss
215− deriving (Eq, Show, Generic, Data, Typeable)
216−
217−-- | Describes the operations available on a single path.
218−-- A @'PathItem'@ may be empty, due to ACL constraints.
219−-- The path itself is still exposed to the documentation viewer
220−-- but they will not know which operations and parameters are available.
221−data PathItem = PathItem
222− { -- | A definition of a GET operation on this path.
223− _pathItemGet :: Maybe Operation
224−
225− -- | A definition of a PUT operation on this path.
226− , _pathItemPut :: Maybe Operation
227−
228− -- | A definition of a POST operation on this path.
229− , _pathItemPost :: Maybe Operation
230−
231− -- | A definition of a DELETE operation on this path.
232− , _pathItemDelete :: Maybe Operation
233−
234− -- | A definition of a OPTIONS operation on this path.
235− , _pathItemOptions :: Maybe Operation
236−
237− -- | A definition of a HEAD operation on this path.
238− , _pathItemHead :: Maybe Operation
239−
240− -- | A definition of a PATCH operation on this path.
241− , _pathItemPatch :: Maybe Operation
242−
243− -- | A list of parameters that are applicable for all the operations described under this path.
244− -- These parameters can be overridden at the operation level, but cannot be removed there.
245− -- The list MUST NOT include duplicated parameters.
246− -- A unique parameter is defined by a combination of a name and location.
247− , _pathItemParameters :: [Referenced Param]
248− } deriving (Eq, Show, Generic, Data, Typeable)
249−
250−-- | Describes a single API operation on a path.
251−data Operation = Operation
252− { -- | A list of tags for API documentation control.
253− -- Tags can be used for logical grouping of operations by resources or any other qualifier.
254− _operationTags :: Set TagName
255−
256− -- | A short summary of what the operation does.
257− -- For maximum readability in the swagger-ui, this field SHOULD be less than 120 characters.
258− , _operationSummary :: Maybe Text
259−
260− -- | A verbose explanation of the operation behavior.
261− -- GFM syntax can be used for rich text representation.
262− , _operationDescription :: Maybe Text
263−
264− -- | Additional external documentation for this operation.
265− , _operationExternalDocs :: Maybe ExternalDocs
266−
267− -- | Unique string used to identify the operation.
268− -- The id MUST be unique among all operations described in the API.
269− -- Tools and libraries MAY use the it to uniquely identify an operation,
270− -- therefore, it is recommended to follow common programming naming conventions.
271− , _operationOperationId :: Maybe Text
272−
273− -- | A list of MIME types the operation can consume.
274− -- This overrides the @'consumes'@.
275− -- @Just []@ MAY be used to clear the global definition.
276− , _operationConsumes :: Maybe MimeList
277−
278− -- | A list of MIME types the operation can produce.
279− -- This overrides the @'produces'@.
280− -- @Just []@ MAY be used to clear the global definition.
281− , _operationProduces :: Maybe MimeList
282−
283− -- | A list of parameters that are applicable for this operation.
284− -- If a parameter is already defined at the @'PathItem'@,
285− -- the new definition will override it, but can never remove it.
286− -- The list MUST NOT include duplicated parameters.
287− -- A unique parameter is defined by a combination of a name and location.
288− , _operationParameters :: [Referenced Param]
289−
290− -- | The list of possible responses as they are returned from executing this operation.
291− , _operationResponses :: Responses
292−
293− -- | The transfer protocol for the operation.
294− -- The value overrides @'schemes'@.
295− , _operationSchemes :: Maybe [Scheme]
296−
297− -- | Declares this operation to be deprecated.
298− -- Usage of the declared operation should be refrained.
299− -- Default value is @False@.
300− , _operationDeprecated :: Maybe Bool
301−
302− -- | A declaration of which security schemes are applied for this operation.
303− -- The list of values describes alternative security schemes that can be used
304− -- (that is, there is a logical OR between the security requirements).
305− -- This definition overrides any declared top-level security.
306− -- To remove a top-level security declaration, @Just []@ can be used.
307− , _operationSecurity :: [SecurityRequirement]
308− } deriving (Eq, Show, Generic, Data, Typeable)
309−
310−newtype MimeList = MimeList { getMimeList :: [MediaType] }
311− deriving (Eq, Show, Semigroup, Monoid, Typeable)
312−
313−mimeListConstr :: Constr
314−mimeListConstr = mkConstr mimeListDataType "MimeList" ["getMimeList"] Prefix
315−
316−mimeListDataType :: DataType
317−mimeListDataType = mkDataType "Data.Swagger.MimeList" [mimeListConstr]
318−
319−instance Data MimeList where
320− gunfold k z c = case constrIndex c of
321− 1 -> k (z (\xs -> MimeList (map fromString xs)))
322− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type MimeList."
323− toConstr (MimeList _) = mimeListConstr
324− dataTypeOf _ = mimeListDataType
325−
326−-- | Describes a single operation parameter.
327−-- A unique parameter is defined by a combination of a name and location.
328−data Param = Param
329− { -- | The name of the parameter.
330− -- Parameter names are case sensitive.
331− _paramName :: Text
332−
333− -- | A brief description of the parameter.
334− -- This could contain examples of use.
335− -- GFM syntax can be used for rich text representation.
336− , _paramDescription :: Maybe Text
337−
338− -- | Determines whether this parameter is mandatory.
339− -- If the parameter is in "path", this property is required and its value MUST be true.
340− -- Otherwise, the property MAY be included and its default value is @False@.
341− , _paramRequired :: Maybe Bool
342−
343− -- | Parameter schema.
344− , _paramSchema :: ParamAnySchema
345− } deriving (Eq, Show, Generic, Data, Typeable)
346−
347−data ParamAnySchema
348− = ParamBody (Referenced Schema)
349− | ParamOther ParamOtherSchema
350− deriving (Eq, Show, Generic, Data, Typeable)
351−
352−data ParamOtherSchema = ParamOtherSchema
353− { -- | The location of the parameter.
354− _paramOtherSchemaIn :: ParamLocation
355−
356− -- | Sets the ability to pass empty-valued parameters.
357− -- This is valid only for either @'ParamQuery'@ or @'ParamFormData'@
358− -- and allows you to send a parameter with a name only or an empty value.
359− -- Default value is @False@.
360− , _paramOtherSchemaAllowEmptyValue :: Maybe Bool
361−
362− , _paramOtherSchemaParamSchema :: ParamSchema 'SwaggerKindParamOtherSchema
363− } deriving (Eq, Show, Generic, Typeable, Data)
364−
365−-- | Items for @'SwaggerArray'@ schemas.
366−--
367−-- @'SwaggerItemsPrimitive'@ should be used only for query params, headers and path pieces.
368−-- The @'CollectionFormat' t@ parameter specifies how elements of an array should be displayed.
369−-- Note that @fmt@ in @'SwaggerItemsPrimitive' fmt schema@ specifies format for elements of type @schema@.
370−-- This is different from the original Swagger's <http://swagger.io/specification/#itemsObject Items Object>.
371−--
372−-- @'SwaggerItemsObject'@ should be used to specify homogenous array @'Schema'@s.
373−--
374−-- @'SwaggerItemsArray'@ should be used to specify tuple @'Schema'@s.
375−data SwaggerItems t where
376− SwaggerItemsPrimitive :: Maybe (CollectionFormat k) -> ParamSchema k-> SwaggerItems k
377− SwaggerItemsObject :: Referenced Schema -> SwaggerItems 'SwaggerKindSchema
378− SwaggerItemsArray :: [Referenced Schema] -> SwaggerItems 'SwaggerKindSchema
379− deriving (Typeable)
380−
381−deriving instance Eq (SwaggerItems t)
382−deriving instance Show (SwaggerItems t)
383−--deriving instance Typeable (SwaggerItems t)
384−
385−swaggerItemsPrimitiveConstr :: Constr
386−swaggerItemsPrimitiveConstr = mkConstr swaggerItemsDataType "SwaggerItemsPrimitive" [] Prefix
387−
388−swaggerItemsObjectConstr :: Constr
389−swaggerItemsObjectConstr = mkConstr swaggerItemsDataType "SwaggerItemsObject" [] Prefix
390−
391−swaggerItemsArrayConstr :: Constr
392−swaggerItemsArrayConstr = mkConstr swaggerItemsDataType "SwaggerItemsArray" [] Prefix
393−
394−swaggerItemsDataType :: DataType
395−swaggerItemsDataType = mkDataType "Data.Swagger.SwaggerItems" [swaggerItemsPrimitiveConstr]
396−
397−-- Note: unfortunately we have to write these Data instances by hand,
398−-- to get better contexts / avoid duplicate name when using standalone deriving
399−
400−instance Data t => Data (SwaggerItems ('SwaggerKindNormal t)) where
401− -- TODO: define gfoldl
402− gunfold k z c = case constrIndex c of
403− 1 -> k (k (z SwaggerItemsPrimitive))
404− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems t)."
405− toConstr _ = swaggerItemsPrimitiveConstr
406− dataTypeOf _ = swaggerItemsDataType
407−
408−-- SwaggerItems SwaggerKindParamOtherSchema can be constructed using SwaggerItemsPrimitive only
409−instance Data (SwaggerItems 'SwaggerKindParamOtherSchema) where
410− -- TODO: define gfoldl
411− gunfold k z c = case constrIndex c of
412− 1 -> k (k (z SwaggerItemsPrimitive))
413− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems SwaggerKindParamOtherSchema)."
414− toConstr _ = swaggerItemsPrimitiveConstr
415− dataTypeOf _ = swaggerItemsDataType
416−
417−instance Data (SwaggerItems 'SwaggerKindSchema) where
418− gfoldl _ _ (SwaggerItemsPrimitive _ _) = error $ " Data.Data.gfoldl: Constructor SwaggerItemsPrimitive used to construct SwaggerItems SwaggerKindSchema"
419− gfoldl k z (SwaggerItemsObject ref) = z SwaggerItemsObject `k` ref
420− gfoldl k z (SwaggerItemsArray ref) = z SwaggerItemsArray `k` ref
421−
422− gunfold k z c = case constrIndex c of
423− 2 -> k (z SwaggerItemsObject)
424− 3 -> k (z SwaggerItemsArray)
425− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type (SwaggerItems SwaggerKindSchema)."
426−
427− toConstr (SwaggerItemsPrimitive _ _) = error "Not supported"
428− toConstr (SwaggerItemsObject _) = swaggerItemsObjectConstr
429− toConstr (SwaggerItemsArray _) = swaggerItemsArrayConstr
430−
431− dataTypeOf _ = swaggerItemsDataType
432−
433−-- | Type used as a kind to avoid overlapping instances.
434−data SwaggerKind t
435− = SwaggerKindNormal t
436− | SwaggerKindParamOtherSchema
437− | SwaggerKindSchema
438− deriving (Typeable)
439−
440−deriving instance Typeable 'SwaggerKindNormal
441−deriving instance Typeable 'SwaggerKindParamOtherSchema
442−deriving instance Typeable 'SwaggerKindSchema
443−
444−type family SwaggerKindType (k :: SwaggerKind *) :: *
445−type instance SwaggerKindType ('SwaggerKindNormal t) = t
446−type instance SwaggerKindType 'SwaggerKindSchema = Schema
447−type instance SwaggerKindType 'SwaggerKindParamOtherSchema = ParamOtherSchema
448−
449−data SwaggerType t where
450− SwaggerString :: SwaggerType t
451− SwaggerNumber :: SwaggerType t
452− SwaggerInteger :: SwaggerType t
453− SwaggerBoolean :: SwaggerType t
454− SwaggerArray :: SwaggerType t
455− SwaggerFile :: SwaggerType 'SwaggerKindParamOtherSchema
456− SwaggerNull :: SwaggerType 'SwaggerKindSchema
457− SwaggerObject :: SwaggerType 'SwaggerKindSchema
458− deriving (Typeable)
459−
460−deriving instance Eq (SwaggerType t)
461−deriving instance Show (SwaggerType t)
462−
463−swaggerTypeConstr :: Data (SwaggerType t) => SwaggerType t -> Constr
464−swaggerTypeConstr t = mkConstr (dataTypeOf t) (show t) [] Prefix
465−
466−swaggerTypeDataType :: {- Data (SwaggerType t) => -} SwaggerType t -> DataType
467−swaggerTypeDataType _ = mkDataType "Data.Swagger.SwaggerType" swaggerTypeConstrs
468−
469−swaggerCommonTypes :: [SwaggerType k]
470−swaggerCommonTypes = [SwaggerString, SwaggerNumber, SwaggerInteger, SwaggerBoolean, SwaggerArray]
471−
472−swaggerParamTypes :: [SwaggerType 'SwaggerKindParamOtherSchema]
473−swaggerParamTypes = swaggerCommonTypes ++ [SwaggerFile]
474−
475−swaggerSchemaTypes :: [SwaggerType 'SwaggerKindSchema]
476−swaggerSchemaTypes = swaggerCommonTypes ++ [error "SwaggerFile is invalid SwaggerType Schema", SwaggerNull, SwaggerObject]
477−
478−swaggerTypeConstrs :: [Constr]
479−swaggerTypeConstrs = map swaggerTypeConstr (swaggerCommonTypes :: [SwaggerType 'SwaggerKindSchema])
480− ++ [swaggerTypeConstr SwaggerFile, swaggerTypeConstr SwaggerNull, swaggerTypeConstr SwaggerObject]
481−
482−instance Typeable t => Data (SwaggerType ('SwaggerKindNormal t)) where
483− gunfold = gunfoldEnum "SwaggerType" swaggerCommonTypes
484− toConstr = swaggerTypeConstr
485− dataTypeOf = swaggerTypeDataType
486−
487−instance Data (SwaggerType 'SwaggerKindParamOtherSchema) where
488− gunfold = gunfoldEnum "SwaggerType ParamOtherSchema" swaggerParamTypes
489− toConstr = swaggerTypeConstr
490− dataTypeOf = swaggerTypeDataType
491−
492−instance Data (SwaggerType 'SwaggerKindSchema) where
493− gunfold = gunfoldEnum "SwaggerType Schema" swaggerSchemaTypes
494− toConstr = swaggerTypeConstr
495− dataTypeOf = swaggerTypeDataType
496−
497−data ParamLocation
498− = -- | Parameters that are appended to the URL.
499− -- For example, in @/items?id=###@, the query parameter is @id@.
500− ParamQuery
501− -- | Custom headers that are expected as part of the request.
502− | ParamHeader
503− -- | Used together with Path Templating, where the parameter value is actually part of the operation's URL.
504− -- This does not include the host or base path of the API.
505− -- For example, in @/items/{itemId}@, the path parameter is @itemId@.
506− | ParamPath
507− -- | Used to describe the payload of an HTTP request when either @application/x-www-form-urlencoded@
508− -- or @multipart/form-data@ are used as the content type of the request
509− -- (in Swagger's definition, the @consumes@ property of an operation).
510− -- This is the only parameter type that can be used to send files, thus supporting the @'ParamFile'@ type.
511− -- Since form parameters are sent in the payload, they cannot be declared together with a body parameter for the same operation.
512− -- Form parameters have a different format based on the content-type used
513− -- (for further details, consult <http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4>).
514− | ParamFormData
515− deriving (Eq, Show, Generic, Data, Typeable)
516−
517−type Format = Text
518−
519−-- | Determines the format of the array.
520−data CollectionFormat t where
521− -- Comma separated values: @foo,bar@.
522− CollectionCSV :: CollectionFormat t
523− -- Space separated values: @foo bar@.
524− CollectionSSV :: CollectionFormat t
525− -- Tab separated values: @foo\\tbar@.
526− CollectionTSV :: CollectionFormat t
527− -- Pipe separated values: @foo|bar@.
528− CollectionPipes :: CollectionFormat t
529− -- Corresponds to multiple parameter instances
530− -- instead of multiple values for a single instance @foo=bar&foo=baz@.
531− -- This is valid only for parameters in @'ParamQuery'@ or @'ParamFormData'@.
532− CollectionMulti :: CollectionFormat 'SwaggerKindParamOtherSchema
533− deriving (Typeable)
534−
535−deriving instance Eq (CollectionFormat t)
536−deriving instance Show (CollectionFormat t)
537−
538−collectionFormatConstr :: CollectionFormat t -> Constr
539−collectionFormatConstr cf = mkConstr collectionFormatDataType (show cf) [] Prefix
540−
541−collectionFormatDataType :: DataType
542−collectionFormatDataType = mkDataType "Data.Swagger.CollectionFormat" $
543− map collectionFormatConstr collectionCommonFormats
544−
545−collectionCommonFormats :: [CollectionFormat t]
546−collectionCommonFormats = [ CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes ]
547−
548−instance Data t => Data (CollectionFormat ('SwaggerKindNormal t)) where
549− gunfold = gunfoldEnum "CollectionFormat" collectionCommonFormats
550− toConstr = collectionFormatConstr
551− dataTypeOf _ = collectionFormatDataType
552−
553−deriving instance Data (CollectionFormat 'SwaggerKindParamOtherSchema)
554−
555−type ParamName = Text
556−
557−data Schema = Schema
558− { _schemaTitle :: Maybe Text
559− , _schemaDescription :: Maybe Text
560− , _schemaRequired :: [ParamName]
561−
562− , _schemaAllOf :: Maybe [Referenced Schema]
563− , _schemaProperties :: InsOrdHashMap Text (Referenced Schema)
564− , _schemaAdditionalProperties :: Maybe AdditionalProperties
565−
566− , _schemaDiscriminator :: Maybe Text
567− , _schemaReadOnly :: Maybe Bool
568− , _schemaXml :: Maybe Xml
569− , _schemaExternalDocs :: Maybe ExternalDocs
570− , _schemaExample :: Maybe Value
571−
572− , _schemaMaxProperties :: Maybe Integer
573− , _schemaMinProperties :: Maybe Integer
574−
575− , _schemaParamSchema :: ParamSchema 'SwaggerKindSchema
576− } deriving (Eq, Show, Generic, Data, Typeable)
577−
578−-- | A @'Schema'@ with an optional name.
579−-- This name can be used in references.
580−data NamedSchema = NamedSchema
581− { _namedSchemaName :: Maybe Text
582− , _namedSchemaSchema :: Schema
583− } deriving (Eq, Show, Generic, Data, Typeable)
584−
585−-- | Regex pattern for @string@ type.
586−type Pattern = Text
587−
588−data ParamSchema (t :: SwaggerKind *) = ParamSchema
589− { -- | Declares the value of the parameter that the server will use if none is provided,
590− -- for example a @"count"@ to control the number of results per page might default to @100@
591− -- if not supplied by the client in the request.
592− -- (Note: "default" has no meaning for required parameters.)
593− -- Unlike JSON Schema this value MUST conform to the defined type for this parameter.
594− _paramSchemaDefault :: Maybe Value
595−
596− , _paramSchemaType :: SwaggerType t
597− , _paramSchemaFormat :: Maybe Format
598− , _paramSchemaItems :: Maybe (SwaggerItems t)
599− , _paramSchemaMaximum :: Maybe Scientific
600− , _paramSchemaExclusiveMaximum :: Maybe Bool
601− , _paramSchemaMinimum :: Maybe Scientific
602− , _paramSchemaExclusiveMinimum :: Maybe Bool
603− , _paramSchemaMaxLength :: Maybe Integer
604− , _paramSchemaMinLength :: Maybe Integer
605− , _paramSchemaPattern :: Maybe Pattern
606− , _paramSchemaMaxItems :: Maybe Integer
607− , _paramSchemaMinItems :: Maybe Integer
608− , _paramSchemaUniqueItems :: Maybe Bool
609− , _paramSchemaEnum :: Maybe [Value]
610− , _paramSchemaMultipleOf :: Maybe Scientific
611− } deriving (Eq, Show, Generic, Typeable)
612−
613−deriving instance (Typeable k, Data (SwaggerType k), Data (SwaggerItems k)) => Data (ParamSchema k)
614−
615−data Xml = Xml
616− { -- | Replaces the name of the element/attribute used for the described schema property.
617− -- When defined within the @'SwaggerItems'@ (items), it will affect the name of the individual XML elements within the list.
618− -- When defined alongside type being array (outside the items),
619− -- it will affect the wrapping element and only if wrapped is true.
620− -- If wrapped is false, it will be ignored.
621− _xmlName :: Maybe Text
622−
623− -- | The URL of the namespace definition.
624− -- Value SHOULD be in the form of a URL.
625− , _xmlNamespace :: Maybe Text
626−
627− -- | The prefix to be used for the name.
628− , _xmlPrefix :: Maybe Text
629−
630− -- | Declares whether the property definition translates to an attribute instead of an element.
631− -- Default value is @False@.
632− , _xmlAttribute :: Maybe Bool
633−
634− -- | MAY be used only for an array definition.
635− -- Signifies whether the array is wrapped
636− -- (for example, @\<books\>\<book/\>\<book/\>\</books\>@)
637− -- or unwrapped (@\<book/\>\<book/\>@).
638− -- Default value is @False@.
639− -- The definition takes effect only when defined alongside type being array (outside the items).
640− , _xmlWrapped :: Maybe Bool
641− } deriving (Eq, Show, Generic, Data, Typeable)
642−
643−-- | A container for the expected responses of an operation.
644−-- The container maps a HTTP response code to the expected response.
645−-- It is not expected from the documentation to necessarily cover all possible HTTP response codes,
646−-- since they may not be known in advance.
647−-- However, it is expected from the documentation to cover a successful operation response and any known errors.
648−data Responses = Responses
649− { -- | The documentation of responses other than the ones declared for specific HTTP response codes.
650− -- It can be used to cover undeclared responses.
651− _responsesDefault :: Maybe (Referenced Response)
652−
653− -- | Any HTTP status code can be used as the property name (one property per HTTP status code).
654− -- Describes the expected response for those HTTP status codes.
655− , _responsesResponses :: InsOrdHashMap HttpStatusCode (Referenced Response)
656− } deriving (Eq, Show, Generic, Data, Typeable)
657−
658−type HttpStatusCode = Int
659−
660−-- | Describes a single response from an API Operation.
661−data Response = Response
662− { -- | A short description of the response.
663− -- GFM syntax can be used for rich text representation.
664− _responseDescription :: Text
665−
666− -- | A definition of the response structure.
667− -- It can be a primitive, an array or an object.
668− -- If this field does not exist, it means no content is returned as part of the response.
669− -- As an extension to the Schema Object, its root type value may also be "file".
670− -- This SHOULD be accompanied by a relevant produces mime-type.
671− , _responseSchema :: Maybe (Referenced Schema)
672−
673− -- | A list of headers that are sent with the response.
674− , _responseHeaders :: InsOrdHashMap HeaderName Header
675−
676− -- | An example of the response message.
677− , _responseExamples :: Maybe Example
678− } deriving (Eq, Show, Generic, Data, Typeable)
679−
680−instance IsString Response where
681− fromString s = Response (fromString s) Nothing mempty Nothing
682−
683−type HeaderName = Text
684−
685−data Header = Header
686− { -- | A short description of the header.
687− _headerDescription :: Maybe Text
688−
689− , _headerParamSchema :: ParamSchema ('SwaggerKindNormal Header)
690− } deriving (Eq, Show, Generic, Data, Typeable)
691−
692−data Example = Example { getExample :: Map MediaType Value }
693− deriving (Eq, Show, Generic, Typeable)
694−
695−exampleConstr :: Constr
696−exampleConstr = mkConstr exampleDataType "Example" ["getExample"] Prefix
697−
698−exampleDataType :: DataType
699−exampleDataType = mkDataType "Data.Swagger.Example" [exampleConstr]
700−
701−instance Data Example where
702− gunfold k z c = case constrIndex c of
703− 1 -> k (z (\m -> Example (Map.mapKeys fromString m)))
704− _ -> error $ "Data.Data.gunfold: Constructor " ++ show c ++ " is not of type Example."
705− toConstr (Example _) = exampleConstr
706− dataTypeOf _ = exampleDataType
707−
708−-- | The location of the API key.
709−data ApiKeyLocation
710− = ApiKeyQuery
711− | ApiKeyHeader
712− deriving (Eq, Show, Generic, Data, Typeable)
713−
714−data ApiKeyParams = ApiKeyParams
715− { -- | The name of the header or query parameter to be used.
716− _apiKeyName :: Text
717−
718− -- | The location of the API key.
719− , _apiKeyIn :: ApiKeyLocation
720− } deriving (Eq, Show, Generic, Data, Typeable)
721−
722−-- | The authorization URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
723−type AuthorizationURL = Text
724−
725−-- | The token URL to be used for OAuth2 flow. This SHOULD be in the form of a URL.
726−type TokenURL = Text
727−
728−data OAuth2Flow
729− = OAuth2Implicit AuthorizationURL
730− | OAuth2Password TokenURL
731− | OAuth2Application TokenURL
732− | OAuth2AccessCode AuthorizationURL TokenURL
733− deriving (Eq, Show, Generic, Data, Typeable)
734−
735−data OAuth2Params = OAuth2Params
736− { -- | The flow used by the OAuth2 security scheme.
737− _oauth2Flow :: OAuth2Flow
738−
739− -- | The available scopes for the OAuth2 security scheme.
740− , _oauth2Scopes :: InsOrdHashMap Text Text
741− } deriving (Eq, Show, Generic, Data, Typeable)
742−
743−data SecuritySchemeType
744− = SecuritySchemeBasic
745− | SecuritySchemeApiKey ApiKeyParams
746− | SecuritySchemeOAuth2 OAuth2Params
747− deriving (Eq, Show, Generic, Data, Typeable)
748−
749−data SecurityScheme = SecurityScheme
750− { -- | The type of the security scheme.
751− _securitySchemeType :: SecuritySchemeType
752−
753− -- | A short description for security scheme.
754− , _securitySchemeDescription :: Maybe Text
755− } deriving (Eq, Show, Generic, Data, Typeable)
756−
757−-- | Lists the required security schemes to execute this operation.
758−-- The object can have multiple security schemes declared in it which are all required
759−-- (that is, there is a logical AND between the schemes).
760−newtype SecurityRequirement = SecurityRequirement
761− { getSecurityRequirement :: InsOrdHashMap Text [Text]
762− } deriving (Eq, Read, Show, Semigroup, Monoid, ToJSON, FromJSON, Data, Typeable)
763−
764−-- | Tag name.
765−type TagName = Text
766−
767−-- | Allows adding meta data to a single tag that is used by @Operation@.
768−-- It is not mandatory to have a @Tag@ per tag used there.
769−data Tag = Tag
770− { -- | The name of the tag.
771− _tagName :: TagName
772−
773− -- | A short description for the tag.
774− -- GFM syntax can be used for rich text representation.
775− , _tagDescription :: Maybe Text
776−
777− -- | Additional external documentation for this tag.
778− , _tagExternalDocs :: Maybe ExternalDocs
779− } deriving (Eq, Ord, Show, Generic, Data, Typeable)
780−
781−instance IsString Tag where
782− fromString s = Tag (fromString s) Nothing Nothing
783−
784−-- | Allows referencing an external resource for extended documentation.
785−data ExternalDocs = ExternalDocs
786− { -- | A short description of the target documentation.
787− -- GFM syntax can be used for rich text representation.
788− _externalDocsDescription :: Maybe Text
789−
790− -- | The URL for the target documentation.
791− , _externalDocsUrl :: URL
792− } deriving (Eq, Ord, Show, Generic, Data, Typeable)
793−
794−-- | A simple object to allow referencing other definitions in the specification.
795−-- It can be used to reference parameters and responses that are defined at the top level for reuse.
796−newtype Reference = Reference { getReference :: Text }
797− deriving (Eq, Show, Data, Typeable)
798−
799−data Referenced a
800− = Ref Reference
801− | Inline a
802− deriving (Eq, Show, Functor, Data, Typeable)
803−
804−instance IsString a => IsString (Referenced a) where
805− fromString = Inline . fromString
806−
807−newtype URL = URL { getUrl :: Text } deriving (Eq, Ord, Show, ToJSON, FromJSON, Data, Typeable)
808−
809−data AdditionalProperties
810− = AdditionalPropertiesAllowed Bool
811− | AdditionalPropertiesSchema (Referenced Schema)
812− deriving (Eq, Show, Data, Typeable)
813−
814−-- =======================================================================
815−-- Monoid instances
816−-- =======================================================================
817−
818−instance Semigroup Swagger where
819− (<>) = genericMappend
820−instance Monoid Swagger where
821− mempty = genericMempty
822− mappend = (<>)
823−
824−instance Semigroup Info where
825− (<>) = genericMappend
826−instance Monoid Info where
827− mempty = genericMempty
828− mappend = (<>)
829−
830−instance Semigroup Contact where
831− (<>) = genericMappend
832−instance Monoid Contact where
833− mempty = genericMempty
834− mappend = (<>)
835−
836−instance Semigroup PathItem where
837− (<>) = genericMappend
838−instance Monoid PathItem where
839− mempty = genericMempty
840− mappend = (<>)
841−
842−instance Semigroup Schema where
843− (<>) = genericMappend
844−instance Monoid Schema where
845− mempty = genericMempty
846− mappend = (<>)
847−
848−instance Semigroup (ParamSchema t) where
849− (<>) = genericMappend
850−instance Monoid (ParamSchema t) where
851− mempty = genericMempty
852− mappend = (<>)
853−
854−instance Semigroup Param where
855− (<>) = genericMappend
856−instance Monoid Param where
857− mempty = genericMempty
858− mappend = (<>)
859−
860−instance Semigroup ParamOtherSchema where
861− (<>) = genericMappend
862−instance Monoid ParamOtherSchema where
863− mempty = genericMempty
864− mappend = (<>)
865−
866−instance Semigroup Header where
867− (<>) = genericMappend
868−instance Monoid Header where
869− mempty = genericMempty
870− mappend = (<>)
871−
872−instance Semigroup Responses where
873− (<>) = genericMappend
874−instance Monoid Responses where
875− mempty = genericMempty
876− mappend = (<>)
877−
878−instance Semigroup Response where
879− (<>) = genericMappend
880−instance Monoid Response where
881− mempty = genericMempty
882− mappend = (<>)
883−
884−instance Semigroup ExternalDocs where
885− (<>) = genericMappend
886−instance Monoid ExternalDocs where
887− mempty = genericMempty
888− mappend = (<>)
889−
890−instance Semigroup Operation where
891− (<>) = genericMappend
892−instance Monoid Operation where
893− mempty = genericMempty
894− mappend = (<>)
895−
896−instance Semigroup Example where
897− (<>) = genericMappend
898−instance Monoid Example where
899− mempty = genericMempty
900− mappend = (<>)
901−
902−-- =======================================================================
903−-- SwaggerMonoid helper instances
904−-- =======================================================================
905−
906−instance SwaggerMonoid Info
907−instance SwaggerMonoid PathItem
908−instance SwaggerMonoid Schema
909−instance SwaggerMonoid (ParamSchema t)
910−instance SwaggerMonoid Param
911−instance SwaggerMonoid ParamOtherSchema
912−instance SwaggerMonoid Responses
913−instance SwaggerMonoid Response
914−instance SwaggerMonoid ExternalDocs
915−instance SwaggerMonoid Operation
916−
917−instance SwaggerMonoid MimeList
918−deriving instance SwaggerMonoid URL
919−
920−instance SwaggerMonoid (SwaggerType t) where
921− swaggerMempty = SwaggerString
922− swaggerMappend _ y = y
923−
924−instance SwaggerMonoid ParamLocation where
925− swaggerMempty = ParamQuery
926− swaggerMappend _ y = y
927−
928−instance OVERLAPPING_ SwaggerMonoid (InsOrdHashMap FilePath PathItem) where
929− swaggerMempty = InsOrdHashMap.empty
930− swaggerMappend = InsOrdHashMap.unionWith mappend
931−
932−instance Monoid a => SwaggerMonoid (Referenced a) where
933− swaggerMempty = Inline mempty
934− swaggerMappend (Inline x) (Inline y) = Inline (mappend x y)
935− swaggerMappend _ y = y
936−
937−instance SwaggerMonoid ParamAnySchema where
938− swaggerMempty = ParamOther swaggerMempty
939− swaggerMappend (ParamBody x) (ParamBody y) = ParamBody (swaggerMappend x y)
940− swaggerMappend (ParamOther x) (ParamOther y) = ParamOther (swaggerMappend x y)
941− swaggerMappend _ y = y
942−
943−-- =======================================================================
944−-- Simple Generic-based ToJSON instances
945−-- =======================================================================
946−
947−instance ToJSON ParamLocation where
948− toJSON = genericToJSON (jsonPrefix "Param")
949−
950−instance ToJSON Info where
951− toJSON = genericToJSON (jsonPrefix "Info")
952−
953−instance ToJSON Contact where
954− toJSON = genericToJSON (jsonPrefix "Contact")
955−
956−instance ToJSON License where
957− toJSON = genericToJSON (jsonPrefix "License")
958−
959−instance ToJSON ApiKeyLocation where
960− toJSON = genericToJSON (jsonPrefix "ApiKey")
961−
962−instance ToJSON ApiKeyParams where
963− toJSON = genericToJSON (jsonPrefix "apiKey")
964−
965−instance ToJSON Scheme where
966− toJSON = genericToJSON (jsonPrefix "")
967−
968−instance ToJSON Tag where
969− toJSON = genericToJSON (jsonPrefix "Tag")
970−
971−instance ToJSON ExternalDocs where
972− toJSON = genericToJSON (jsonPrefix "ExternalDocs")
973−
974−instance ToJSON Xml where
975− toJSON = genericToJSON (jsonPrefix "Xml")
976−
977−-- =======================================================================
978−-- Simple Generic-based FromJSON instances
979−-- =======================================================================
980−
981−instance FromJSON ParamLocation where
982− parseJSON = genericParseJSON (jsonPrefix "Param")
983−
984−instance FromJSON Info where
985− parseJSON = genericParseJSON (jsonPrefix "Info")
986−
987−instance FromJSON Contact where
988− parseJSON = genericParseJSON (jsonPrefix "Contact")
989−
990−instance FromJSON License where
991− parseJSON = genericParseJSON (jsonPrefix "License")
992−
993−instance FromJSON ApiKeyLocation where
994− parseJSON = genericParseJSON (jsonPrefix "ApiKey")
995−
996−instance FromJSON ApiKeyParams where
997− parseJSON = genericParseJSON (jsonPrefix "apiKey")
998−
999−instance FromJSON Scheme where
1000− parseJSON = genericParseJSON (jsonPrefix "")
1001−
1002−instance FromJSON Tag where
1003− parseJSON = genericParseJSON (jsonPrefix "Tag")
1004−
1005−instance FromJSON ExternalDocs where
1006− parseJSON = genericParseJSON (jsonPrefix "ExternalDocs")
1007−
1008−-- =======================================================================
1009−-- Manual ToJSON instances
1010−-- =======================================================================
1011−
1012−instance ToJSON OAuth2Flow where
1013− toJSON (OAuth2Implicit authUrl) = object
1014− [ "flow" .= ("implicit" :: Text)
1015− , "authorizationUrl" .= authUrl ]
1016− toJSON (OAuth2Password tokenUrl) = object
1017− [ "flow" .= ("password" :: Text)
1018− , "tokenUrl" .= tokenUrl ]
1019− toJSON (OAuth2Application tokenUrl) = object
1020− [ "flow" .= ("application" :: Text)
1021− , "tokenUrl" .= tokenUrl ]
1022− toJSON (OAuth2AccessCode authUrl tokenUrl) = object
1023− [ "flow" .= ("accessCode" :: Text)
1024− , "authorizationUrl" .= authUrl
1025− , "tokenUrl" .= tokenUrl ]
1026−
1027−instance ToJSON OAuth2Params where
1028− toJSON = sopSwaggerGenericToJSON
1029− DEFINE_TOENCODING
1030−
1031−instance ToJSON SecuritySchemeType where
1032− toJSON SecuritySchemeBasic
1033− = object [ "type" .= ("basic" :: Text) ]
1034− toJSON (SecuritySchemeApiKey params)
1035− = toJSON params
1036− <+> object [ "type" .= ("apiKey" :: Text) ]
1037− toJSON (SecuritySchemeOAuth2 params)
1038− = toJSON params
1039− <+> object [ "type" .= ("oauth2" :: Text) ]
1040−
1041−instance ToJSON Swagger where
1042− toJSON = sopSwaggerGenericToJSON
1043− DEFINE_TOENCODING
1044−
1045−instance ToJSON SecurityScheme where
1046− toJSON = sopSwaggerGenericToJSON
1047− DEFINE_TOENCODING
1048−
1049−instance ToJSON Schema where
1050− toJSON = sopSwaggerGenericToJSON
1051− DEFINE_TOENCODING
1052−
1053−instance ToJSON Header where
1054− toJSON = sopSwaggerGenericToJSON
1055− DEFINE_TOENCODING
1056−
1057−-- | As for nullary schema for 0-arity type constructors, see
1058−-- <https://github.com/GetShopTV/swagger2/issues/167>.
1059−--
1060−-- >>> encode (SwaggerItemsArray [])
1061−-- "{\"example\":[],\"items\":{},\"maxItems\":0}"
1062−--
1063−instance ToJSON (ParamSchema t) => ToJSON (SwaggerItems t) where
1064− toJSON (SwaggerItemsPrimitive fmt schema) = object
1065− [ "collectionFormat" .= fmt
1066− , "items" .= schema ]
1067− toJSON (SwaggerItemsObject x) = object [ "items" .= x ]
1068− toJSON (SwaggerItemsArray []) = object
1069− [ "items" .= object []
1070− , "maxItems" .= (0 :: Int)
1071− , "example" .= Array mempty
1072− ]
1073− toJSON (SwaggerItemsArray x) = object [ "items" .= x ]
1074−
1075−instance ToJSON Host where
1076− toJSON (Host host mport) = toJSON $
1077− case mport of
1078− Nothing -> host
1079− Just port -> host ++ ":" ++ show port
1080−
1081−instance ToJSON MimeList where
1082− toJSON (MimeList xs) = toJSON (map show xs)
1083−
1084−instance ToJSON Param where
1085− toJSON = sopSwaggerGenericToJSON
1086− DEFINE_TOENCODING
1087−
1088−instance ToJSON ParamAnySchema where
1089− toJSON (ParamBody s) = object [ "in" .= ("body" :: Text), "schema" .= s ]
1090− toJSON (ParamOther s) = toJSON s
1091−
1092−instance ToJSON ParamOtherSchema where
1093− toJSON = sopSwaggerGenericToJSON
1094− DEFINE_TOENCODING
1095−
1096−instance ToJSON Responses where
1097− toJSON = sopSwaggerGenericToJSON
1098− DEFINE_TOENCODING
1099−
1100−instance ToJSON Response where
1101− toJSON = sopSwaggerGenericToJSON
1102− DEFINE_TOENCODING
1103−
1104−instance ToJSON Operation where
1105− toJSON = sopSwaggerGenericToJSON
1106− DEFINE_TOENCODING
1107−
1108−instance ToJSON PathItem where
1109− toJSON = sopSwaggerGenericToJSON
1110− DEFINE_TOENCODING
1111−
1112−instance ToJSON Example where
1113− toJSON = toJSON . Map.mapKeys show . getExample
1114−
1115−instance ToJSON Reference where
1116− toJSON (Reference ref) = object [ "$ref" .= ref ]
1117−
1118−referencedToJSON :: ToJSON a => Text -> Referenced a -> Value
1119−referencedToJSON prefix (Ref (Reference ref)) = object [ "$ref" .= (prefix <> ref) ]
1120−referencedToJSON _ (Inline x) = toJSON x
1121−
1122−instance ToJSON (Referenced Schema) where toJSON = referencedToJSON "#/definitions/"
1123−instance ToJSON (Referenced Param) where toJSON = referencedToJSON "#/parameters/"
1124−instance ToJSON (Referenced Response) where toJSON = referencedToJSON "#/responses/"
1125−
1126−instance ToJSON (SwaggerType t) where
1127− toJSON SwaggerArray = "array"
1128− toJSON SwaggerString = "string"
1129− toJSON SwaggerInteger = "integer"
1130− toJSON SwaggerNumber = "number"
1131− toJSON SwaggerBoolean = "boolean"
1132− toJSON SwaggerFile = "file"
1133− toJSON SwaggerNull = "null"
1134− toJSON SwaggerObject = "object"
1135−
1136−instance ToJSON (CollectionFormat t) where
1137− toJSON CollectionCSV = "csv"
1138− toJSON CollectionSSV = "ssv"
1139− toJSON CollectionTSV = "tsv"
1140− toJSON CollectionPipes = "pipes"
1141− toJSON CollectionMulti = "multi"
1142−
1143−instance ToJSON (ParamSchema k) where
1144− -- TODO: this is a bit fishy, why we need sub object only in `ToJSON`?
1145− toJSON = sopSwaggerGenericToJSONWithOpts $
1146− mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1147−
1148−instance ToJSON AdditionalProperties where
1149− toJSON (AdditionalPropertiesAllowed b) = toJSON b
1150− toJSON (AdditionalPropertiesSchema s) = toJSON s
1151−
1152−-- =======================================================================
1153−-- Manual FromJSON instances
1154−-- =======================================================================
1155−
1156−instance FromJSON OAuth2Flow where
1157− parseJSON (Object o) = do
1158− (flow :: Text) <- o .: "flow"
1159− case flow of
1160− "implicit" -> OAuth2Implicit <$> o .: "authorizationUrl"
1161− "password" -> OAuth2Password <$> o .: "tokenUrl"
1162− "application" -> OAuth2Application <$> o .: "tokenUrl"
1163− "accessCode" -> OAuth2AccessCode
1164− <$> o .: "authorizationUrl"
1165− <*> o .: "tokenUrl"
1166− _ -> empty
1167− parseJSON _ = empty
1168−
1169−instance FromJSON OAuth2Params where
1170− parseJSON = sopSwaggerGenericParseJSON
1171−
1172−instance FromJSON SecuritySchemeType where
1173− parseJSON js@(Object o) = do
1174− (t :: Text) <- o .: "type"
1175− case t of
1176− "basic" -> pure SecuritySchemeBasic
1177− "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1178− "oauth2" -> SecuritySchemeOAuth2 <$> parseJSON js
1179− _ -> empty
1180− parseJSON _ = empty
1181−
1182−instance FromJSON Swagger where
1183− parseJSON = sopSwaggerGenericParseJSON
1184−
1185−instance FromJSON SecurityScheme where
1186− parseJSON = sopSwaggerGenericParseJSON
1187−
1188−instance FromJSON Schema where
1189− parseJSON = fmap nullaryCleanup . sopSwaggerGenericParseJSON
1190− where nullaryCleanup :: Schema -> Schema
1191− nullaryCleanup s@Schema{_schemaParamSchema=ps} =
1192− if _paramSchemaItems ps == Just (SwaggerItemsArray [])
1193− then s { _schemaExample = Nothing
1194− , _schemaParamSchema = ps { _paramSchemaMaxItems = Nothing } }
1195− else s
1196−
1197−instance FromJSON Header where
1198− parseJSON = sopSwaggerGenericParseJSON
1199−
1200−instance (FromJSON (CollectionFormat ('SwaggerKindNormal t)), FromJSON (ParamSchema ('SwaggerKindNormal t))) => FromJSON (SwaggerItems ('SwaggerKindNormal t)) where
1201− parseJSON = withObject "SwaggerItemsPrimitive" $ \o -> SwaggerItemsPrimitive
1202− <$> o .:? "collectionFormat"
1203− <*> (o .: "items" >>= parseJSON)
1204−
1205−instance FromJSON (SwaggerItems 'SwaggerKindParamOtherSchema) where
1206− parseJSON = withObject "SwaggerItemsPrimitive" $ \o -> SwaggerItemsPrimitive
1207− <$> o .:? "collectionFormat"
1208− <*> ((o .: "items" >>= parseJSON) <|> fail ("foo" ++ show o))
1209−
1210−-- |
1211−--
1212−-- >>> decode "{}" :: Maybe (SwaggerItems 'SwaggerKindSchema)
1213−-- Just (SwaggerItemsArray [])
1214−--
1215−-- >>> eitherDecode "{\"$ref\":\"#/definitions/example\"}" :: Either String (SwaggerItems 'SwaggerKindSchema)
1216−-- Right (SwaggerItemsObject (Ref (Reference {getReference = "example"})))
1217−--
1218−-- >>> eitherDecode "[{\"$ref\":\"#/definitions/example\"}]" :: Either String (SwaggerItems 'SwaggerKindSchema)
1219−-- Right (SwaggerItemsArray [Ref (Reference {getReference = "example"})])
1220−--
1221−instance FromJSON (SwaggerItems 'SwaggerKindSchema) where
1222− parseJSON js@(Object obj)
1223− | null obj = pure $ SwaggerItemsArray [] -- Nullary schema.
1224− | otherwise = SwaggerItemsObject <$> parseJSON js
1225− parseJSON js@(Array _) = SwaggerItemsArray <$> parseJSON js
1226− parseJSON _ = empty
1227−
1228−instance FromJSON Host where
1229− parseJSON (String s) = case map Text.unpack $ Text.split (== ':') s of
1230− [host] -> return $ Host host Nothing
1231− [host, port] -> case readMaybe port of
1232− Nothing -> fail $ "Invalid port `" ++ port ++ "'"
1233− Just p -> return $ Host host (Just (fromInteger p))
1234− _ -> fail $ "Invalid host `" ++ Text.unpack s ++ "'"
1235− parseJSON _ = empty
1236−
1237−instance FromJSON MimeList where
1238− parseJSON js = (MimeList . map fromString) <$> parseJSON js
1239−
1240−instance FromJSON Param where
1241− parseJSON = sopSwaggerGenericParseJSON
1242−
1243−instance FromJSON ParamAnySchema where
1244− parseJSON js@(Object o) = do
1245− (i :: Text) <- o .: "in"
1246− case i of
1247− "body" -> do
1248− schema <- o .: "schema"
1249− ParamBody <$> parseJSON schema
1250− _ -> ParamOther <$> parseJSON js
1251− parseJSON _ = empty
1252−
1253−instance FromJSON ParamOtherSchema where
1254− parseJSON = sopSwaggerGenericParseJSON
1255−
1256−instance FromJSON Responses where
1257− parseJSON (Object o) = Responses
1258− <$> o .:? "default"
1259− <*> (parseJSON (Object (HashMap.delete "default" o)))
1260− parseJSON _ = empty
1261−
1262−instance FromJSON Example where
1263− parseJSON js = do
1264− m <- parseJSON js
1265− pure $ Example (Map.mapKeys fromString m)
1266−
1267−instance FromJSON Response where
1268− parseJSON = sopSwaggerGenericParseJSON
1269−
1270−instance FromJSON Operation where
1271− parseJSON = sopSwaggerGenericParseJSON
1272−
1273−instance FromJSON PathItem where
1274− parseJSON = sopSwaggerGenericParseJSON
1275−
1276−instance FromJSON Reference where
1277− parseJSON (Object o) = Reference <$> o .: "$ref"
1278− parseJSON _ = empty
1279−
1280−referencedParseJSON :: FromJSON a => Text -> Value -> JSON.Parser (Referenced a)
1281−referencedParseJSON prefix js@(Object o) = do
1282− ms <- o .:? "$ref"
1283− case ms of
1284− Nothing -> Inline <$> parseJSON js
1285− Just s -> Ref <$> parseRef s
1286− where
1287− parseRef s = do
1288− case Text.stripPrefix prefix s of
1289− Nothing -> fail $ "expected $ref of the form \"" <> Text.unpack prefix <> "*\", but got " <> show s
1290− Just suffix -> pure (Reference suffix)
1291−referencedParseJSON _ _ = fail "referenceParseJSON: not an object"
1292−
1293−instance FromJSON (Referenced Schema) where parseJSON = referencedParseJSON "#/definitions/"
1294−instance FromJSON (Referenced Param) where parseJSON = referencedParseJSON "#/parameters/"
1295−instance FromJSON (Referenced Response) where parseJSON = referencedParseJSON "#/responses/"
1296−
1297−instance FromJSON Xml where
1298− parseJSON = genericParseJSON (jsonPrefix "xml")
1299−
1300−instance FromJSON (SwaggerType 'SwaggerKindSchema) where
1301− parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray, SwaggerNull, SwaggerObject]
1302−
1303−instance FromJSON (SwaggerType 'SwaggerKindParamOtherSchema) where
1304− parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray, SwaggerFile]
1305−
1306−instance FromJSON (SwaggerType ('SwaggerKindNormal t)) where
1307− parseJSON = parseOneOf [SwaggerString, SwaggerInteger, SwaggerNumber, SwaggerBoolean, SwaggerArray]
1308−
1309−instance FromJSON (CollectionFormat ('SwaggerKindNormal t)) where
1310− parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes]
1311−
1312−-- NOTE: There aren't collections of 'Schema'
1313−--instance FromJSON (CollectionFormat (SwaggerKindSchema)) where
1314−-- parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes]
1315−
1316−instance FromJSON (CollectionFormat 'SwaggerKindParamOtherSchema) where
1317− parseJSON = parseOneOf [CollectionCSV, CollectionSSV, CollectionTSV, CollectionPipes, CollectionMulti]
1318−
1319−instance (FromJSON (SwaggerType ('SwaggerKindNormal t)), FromJSON (SwaggerItems ('SwaggerKindNormal t))) => FromJSON (ParamSchema ('SwaggerKindNormal t)) where
1320− parseJSON = sopSwaggerGenericParseJSON
1321−instance FromJSON (ParamSchema 'SwaggerKindParamOtherSchema) where
1322− parseJSON = sopSwaggerGenericParseJSON
1323−instance FromJSON (ParamSchema 'SwaggerKindSchema) where
1324− parseJSON = sopSwaggerGenericParseJSON
1325−
1326−instance FromJSON AdditionalProperties where
1327− parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
1328− parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
1329−
1330−-------------------------------------------------------------------------------
1331−-- TH splices
1332−-------------------------------------------------------------------------------
1333−
1334−deriveGeneric ''Header
1335−deriveGeneric ''OAuth2Params
1336−deriveGeneric ''Operation
1337−deriveGeneric ''Param
1338−deriveGeneric ''ParamOtherSchema
1339−deriveGeneric ''PathItem
1340−deriveGeneric ''Response
1341−deriveGeneric ''Responses
1342−deriveGeneric ''SecurityScheme
1343−deriveGeneric ''Schema
1344−deriveGeneric ''ParamSchema
1345−deriveGeneric ''Swagger
1346−
1347−instance HasSwaggerAesonOptions Header where
1348− swaggerAesonOptions _ = mkSwaggerAesonOptions "header" & saoSubObject ?~ "paramSchema"
1349−instance HasSwaggerAesonOptions OAuth2Params where
1350− swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "flow"
1351−instance HasSwaggerAesonOptions Operation where
1352− swaggerAesonOptions _ = mkSwaggerAesonOptions "operation"
1353−instance HasSwaggerAesonOptions Param where
1354− swaggerAesonOptions _ = mkSwaggerAesonOptions "param" & saoSubObject ?~ "schema"
1355−instance HasSwaggerAesonOptions ParamOtherSchema where
1356− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramOtherSchema" & saoSubObject ?~ "paramSchema"
1357−instance HasSwaggerAesonOptions PathItem where
1358− swaggerAesonOptions _ = mkSwaggerAesonOptions "pathItem"
1359−instance HasSwaggerAesonOptions Response where
1360− swaggerAesonOptions _ = mkSwaggerAesonOptions "response"
1361−instance HasSwaggerAesonOptions Responses where
1362− swaggerAesonOptions _ = mkSwaggerAesonOptions "responses" & saoSubObject ?~ "responses"
1363−instance HasSwaggerAesonOptions SecurityScheme where
1364− swaggerAesonOptions _ = mkSwaggerAesonOptions "securityScheme" & saoSubObject ?~ "type"
1365−instance HasSwaggerAesonOptions Schema where
1366− swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1367−instance HasSwaggerAesonOptions Swagger where
1368− swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("swagger", "2.0")]
1369−
1370−instance HasSwaggerAesonOptions (ParamSchema ('SwaggerKindNormal t)) where
1371− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1372−instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindParamOtherSchema) where
1373− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema" & saoSubObject ?~ "items"
1374−-- NOTE: Schema doesn't have 'items' sub object!
1375−instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindSchema) where
1376− swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema"
1377−
1378−instance AesonDefaultValue (ParamSchema s)
1379−instance AesonDefaultValue OAuth2Flow
1380−instance AesonDefaultValue Responses
1381−instance AesonDefaultValue ParamAnySchema
1382−instance AesonDefaultValue SecuritySchemeType
1383−instance AesonDefaultValue (SwaggerType a)
1384−instance AesonDefaultValue MimeList where defaultValue = Just mempty
1385−instance AesonDefaultValue Info
1386−instance AesonDefaultValue ParamLocation
src/Data/Swagger/Lens.hsdeleted−180
@@ -1,180 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE TemplateHaskell #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE DataKinds #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−{-# OPTIONS_GHC -fno-warn-orphans #-}
13−#include "overlapping-compat.h"
14−-- |
15−-- Module: Data.Swagger.Lens
16−-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
17−-- Stability: experimental
18−--
19−-- Lenses and prisms for Swagger.
20−module Data.Swagger.Lens where
21−
22−import Control.Lens
23−import Data.Aeson (Value)
24−import Data.Scientific (Scientific)
25−import Data.Swagger.Internal
26−import Data.Swagger.Internal.Utils
27−import Data.Text (Text)
28−
29−-- * Classy lenses
30−
31−makeFields ''Swagger
32−makeFields ''Host
33−makeFields ''Info
34−makeFields ''Contact
35−makeFields ''License
36−makeLensesWith swaggerFieldRules ''PathItem
37−makeFields ''Tag
38−makeFields ''Operation
39−makeFields ''Param
40−makeLensesWith swaggerFieldRules ''ParamOtherSchema
41−makeFields ''Header
42−makeFields ''Schema
43−makeFields ''NamedSchema
44−makeLensesWith swaggerFieldRules ''ParamSchema
45−makeFields ''Xml
46−makeLensesWith swaggerFieldRules ''Responses
47−makeFields ''Response
48−makeLensesWith swaggerFieldRules ''SecurityScheme
49−makeFields ''ApiKeyParams
50−makeFields ''OAuth2Params
51−makeFields ''ExternalDocs
52−
53−-- * Prisms
54−-- ** 'ParamAnySchema' prisms
55−makePrisms ''ParamAnySchema
56−-- ** 'SecuritySchemeType' prisms
57−makePrisms ''SecuritySchemeType
58−-- ** 'Referenced' prisms
59−makePrisms ''Referenced
60−
61−-- ** 'SwaggerItems' prisms
62−
63−_SwaggerItemsArray :: Review (SwaggerItems 'SwaggerKindSchema) [Referenced Schema]
64−_SwaggerItemsArray
65− = unto (\x -> SwaggerItemsArray x)
66−{- \x -> case x of
67− SwaggerItemsPrimitive c p -> Left (SwaggerItemsPrimitive c p)
68− SwaggerItemsObject o -> Left (SwaggerItemsObject o)
69− SwaggerItemsArray a -> Right a
70−-}
71−
72−_SwaggerItemsObject :: Review (SwaggerItems 'SwaggerKindSchema) (Referenced Schema)
73−_SwaggerItemsObject
74− = unto (\x -> SwaggerItemsObject x)
75−{- \x -> case x of
76− SwaggerItemsPrimitive c p -> Left (SwaggerItemsPrimitive c p)
77− SwaggerItemsObject o -> Right o
78− SwaggerItemsArray a -> Left (SwaggerItemsArray a)
79−-}
80−
81−_SwaggerItemsPrimitive :: forall t p f. (Profunctor p, Bifunctor p, Functor f) => Optic' p f (SwaggerItems t) (Maybe (CollectionFormat t), ParamSchema t)
82−_SwaggerItemsPrimitive = unto (\(c, p) -> SwaggerItemsPrimitive c p)
83−
84−-- =============================================================
85−-- More helpful instances for easier access to schema properties
86−
87−type instance Index Responses = HttpStatusCode
88−type instance Index Operation = HttpStatusCode
89−
90−type instance IxValue Responses = Referenced Response
91−type instance IxValue Operation = Referenced Response
92−
93−instance Ixed Responses where ix n = responses . ix n
94−instance At Responses where at n = responses . at n
95−
96−instance Ixed Operation where ix n = responses . ix n
97−instance At Operation where at n = responses . at n
98−
99−instance HasParamSchema NamedSchema (ParamSchema 'SwaggerKindSchema) where paramSchema = schema.paramSchema
100−
101−-- HasType instances
102−instance HasType Header (SwaggerType ('SwaggerKindNormal Header)) where type_ = paramSchema.type_
103−instance HasType Schema (SwaggerType 'SwaggerKindSchema) where type_ = paramSchema.type_
104−instance HasType NamedSchema (SwaggerType 'SwaggerKindSchema) where type_ = paramSchema.type_
105−instance HasType ParamOtherSchema (SwaggerType 'SwaggerKindParamOtherSchema) where type_ = paramSchema.type_
106−
107−-- HasDefault instances
108−instance HasDefault Header (Maybe Value) where default_ = paramSchema.default_
109−instance HasDefault Schema (Maybe Value) where default_ = paramSchema.default_
110−instance HasDefault ParamOtherSchema (Maybe Value) where default_ = paramSchema.default_
111−
112−-- OVERLAPPABLE instances
113−
114−instance
115−#if __GLASGOW_HASKELL__ >= 710
116− OVERLAPPABLE_
117−#endif
118− HasParamSchema s (ParamSchema t)
119− => HasFormat s (Maybe Format) where
120− format = paramSchema.format
121−
122−instance
123−#if __GLASGOW_HASKELL__ >= 710
124− OVERLAPPABLE_
125−#endif
126− HasParamSchema s (ParamSchema t)
127− => HasItems s (Maybe (SwaggerItems t)) where
128− items = paramSchema.items
129−
130−instance
131−#if __GLASGOW_HASKELL__ >= 710
132− OVERLAPPABLE_
133−#endif
134− HasParamSchema s (ParamSchema t)
135− => HasMaximum s (Maybe Scientific) where
136− maximum_ = paramSchema.maximum_
137−
138−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
139− => HasExclusiveMaximum s (Maybe Bool) where
140− exclusiveMaximum = paramSchema.exclusiveMaximum
141−
142−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
143− => HasMinimum s (Maybe Scientific) where
144− minimum_ = paramSchema.minimum_
145−
146−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
147− => HasExclusiveMinimum s (Maybe Bool) where
148− exclusiveMinimum = paramSchema.exclusiveMinimum
149−
150−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
151− => HasMaxLength s (Maybe Integer) where
152− maxLength = paramSchema.maxLength
153−
154−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
155− => HasMinLength s (Maybe Integer) where
156− minLength = paramSchema.minLength
157−
158−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
159− => HasPattern s (Maybe Text) where
160− pattern = paramSchema.pattern
161−
162−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
163− => HasMaxItems s (Maybe Integer) where
164− maxItems = paramSchema.maxItems
165−
166−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
167− => HasMinItems s (Maybe Integer) where
168− minItems = paramSchema.minItems
169−
170−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
171− => HasUniqueItems s (Maybe Bool) where
172− uniqueItems = paramSchema.uniqueItems
173−
174−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
175− => HasEnum s (Maybe [Value]) where
176− enum_ = paramSchema.enum_
177−
178−instance OVERLAPPABLE_ HasParamSchema s (ParamSchema t)
179− => HasMultipleOf s (Maybe Scientific) where
180− multipleOf = paramSchema.multipleOf
src/Data/Swagger/Operation.hsdeleted−192
@@ -1,192 +0,0 @@
1−{-# LANGUAGE RankNTypes #-}
2−-- |
3−-- Module: Data.Swagger.Operation
4−-- Maintainer: Nickolay Kudasov <nickolay@getshoptv.com>
5−-- Stability: experimental
6−--
7−-- Helper traversals and functions for Swagger operations manipulations.
8−-- These might be useful when you already have Swagger specification
9−-- generated by something else.
10−module Data.Swagger.Operation (
11− -- * Operation traversals
12− allOperations,
13− operationsOf,
14−
15− -- * Manipulation
16− -- ** Tags
17− applyTags,
18− applyTagsFor,
19−
20− -- ** Responses
21− setResponse,
22− setResponseWith,
23− setResponseFor,
24− setResponseForWith,
25−
26− -- ** Paths
27− prependPath,
28−
29− -- * Miscellaneous
30− declareResponse,
31−) where
32−
33−import Prelude ()
34−import Prelude.Compat
35−
36−import Control.Lens
37−import Data.Data.Lens
38−import Data.List.Compat
39−import Data.Maybe (mapMaybe)
40−import Data.Proxy
41−import qualified Data.Set as Set
42−
43−import Data.Swagger.Declare
44−import Data.Swagger.Internal
45−import Data.Swagger.Lens
46−import Data.Swagger.Schema
47−
48−import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
49−
50−-- $setup
51−-- >>> import Data.Aeson
52−-- >>> import Data.Proxy
53−-- >>> import Data.Time
54−
55−-- | Prepend path piece to all operations of the spec.
56−-- Leading and trailing slashes are trimmed/added automatically.
57−--
58−-- >>> let api = (mempty :: Swagger) & paths .~ [("/info", mempty)]
59−-- >>> encode $ prependPath "user/{user_id}" api ^. paths
60−-- "{\"/user/{user_id}/info\":{}}"
61−prependPath :: FilePath -> Swagger -> Swagger
62−prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
63− where
64− x </> y = case trim y of
65− "" -> "/" <> trim x
66− y' -> "/" <> trim x <> "/" <> y'
67−
68− trim = dropWhile (== '/') . dropWhileEnd (== '/')
69−
70−-- | All operations of a Swagger spec.
71−allOperations :: Traversal' Swagger Operation
72−allOperations = paths.traverse.template
73−
74−-- | @'operationsOf' sub@ will traverse only those operations
75−-- that are present in @sub@. Note that @'Operation'@ is determined
76−-- by both path and method.
77−--
78−-- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"
79−-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
80−-- >>> let sub = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
81−-- >>> encode api
82−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
83−-- >>> encode $ api & operationsOf sub . at 404 ?~ "Not found"
84−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"404\":{\"description\":\"Not found\"},\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
85−operationsOf :: Swagger -> Traversal' Swagger Operation
86−operationsOf sub = paths.itraversed.withIndex.subops
87− where
88− -- | Traverse operations that correspond to paths and methods of the sub API.
89− subops :: Traversal' (FilePath, PathItem) Operation
90− subops f (path, item) = case InsOrdHashMap.lookup path (sub ^. paths) of
91− Just subitem -> (,) path <$> methodsOf subitem f item
92− Nothing -> pure (path, item)
93−
94− -- | Traverse operations that exist in a given @'PathItem'@
95− -- This is used to traverse only the operations that exist in sub API.
96− methodsOf :: PathItem -> Traversal' PathItem Operation
97− methodsOf pathItem = partsOf template . itraversed . indices (`elem` ns) . _Just
98− where
99− ops = pathItem ^.. template :: [Maybe Operation]
100− ns = mapMaybe (fmap fst . sequenceA) $ zip [0..] ops
101−
102−-- | Apply tags to all operations and update the global list of tags.
103−--
104−-- @
105−-- 'applyTags' = 'applyTagsFor' 'allOperations'
106−-- @
107−applyTags :: [Tag] -> Swagger -> Swagger
108−applyTags = applyTagsFor allOperations
109−
110−-- | Apply tags to a part of Swagger spec and update the global
111−-- list of tags.
112−applyTagsFor :: Traversal' Swagger Operation -> [Tag] -> Swagger -> Swagger
113−applyTagsFor ops ts swag = swag
114− & ops . tags %~ (<> Set.fromList (map _tagName ts))
115− & tags %~ (<> Set.fromList ts)
116−
117−-- | Construct a response with @'Schema'@ while declaring all
118−-- necessary schema definitions.
119−--
120−-- >>> encode $ runDeclare (declareResponse (Proxy :: Proxy Day)) mempty
121−-- "[{\"Day\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}},{\"description\":\"\",\"schema\":{\"$ref\":\"#/definitions/Day\"}}]"
122−declareResponse :: ToSchema a => Proxy a -> Declare (Definitions Schema) Response
123−declareResponse proxy = do
124− s <- declareSchemaRef proxy
125− return (mempty & schema ?~ s)
126−
127−-- | Set response for all operations.
128−-- This will also update global schema definitions.
129−--
130−-- If the response already exists it will be overwritten.
131−--
132−-- @
133−-- 'setResponse' = 'setResponseFor' 'allOperations'
134−-- @
135−--
136−-- Example:
137−--
138−-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
139−-- >>> let res = declareResponse (Proxy :: Proxy Day)
140−-- >>> encode $ api & setResponse 200 res
141−-- "{\"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\"}}}"
142−--
143−-- See also @'setResponseWith'@.
144−setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
145−setResponse = setResponseFor allOperations
146−
147−-- | Set or update response for all operations.
148−-- This will also update global schema definitions.
149−--
150−-- If the response already exists, but it can't be dereferenced (invalid @\$ref@),
151−-- then just the new response is used.
152−--
153−-- @
154−-- 'setResponseWith' = 'setResponseForWith' 'allOperations'
155−-- @
156−--
157−-- See also @'setResponse'@.
158−setResponseWith :: (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
159−setResponseWith = setResponseForWith allOperations
160−
161−-- | Set response for specified operations.
162−-- This will also update global schema definitions.
163−--
164−-- If the response already exists it will be overwritten.
165−--
166−-- See also @'setResponseForWith'@.
167−setResponseFor :: Traversal' Swagger Operation -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
168−setResponseFor ops code dres swag = swag
169− & definitions %~ (<> defs)
170− & ops . at code ?~ Inline res
171− where
172− (defs, res) = runDeclare dres mempty
173−
174−-- | Set or update response for specified operations.
175−-- This will also update global schema definitions.
176−--
177−-- If the response already exists, but it can't be dereferenced (invalid @\$ref@),
178−-- then just the new response is used.
179−--
180−-- See also @'setResponseFor'@.
181−setResponseForWith :: Traversal' Swagger Operation -> (Response -> Response -> Response) -> HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
182−setResponseForWith ops f code dres swag = swag
183− & definitions %~ (<> defs)
184− & ops . at code %~ Just . Inline . combine
185− where
186− (defs, new) = runDeclare dres mempty
187−
188− combine (Just (Ref (Reference n))) = case swag ^. responses.at n of
189− Just old -> f old new
190− Nothing -> new -- response name can't be dereferenced, replacing with new response
191− combine (Just (Inline old)) = f old new
192− combine Nothing = new
stack.yaml+6−4
@@ -1,7 +1,9 @@
1−resolver: lts-12.10
1+resolver: lts-16.31
22packages:
33- '.'
44extra-deps:
5−- aeson-1.4.2.0
6−- base-compat-batteries-0.10.4
7−- contravariant-1.5
5+- optics-core-0.3
6+- optics-th-0.3
7+- optics-extra-0.3
8+- indexed-profunctors-0.1
9+- insert-ordered-containers-0.2.3.1
swagger2.cabaldeleted−151
@@ -1,151 +0,0 @@
1−cabal-version: >=1.10
2−name: swagger2
3−version: 2.3.1.1
4−
5−synopsis: Swagger 2.0 data model
6−category: Web, Swagger
7−description:
8− This library is intended to be used for decoding and encoding Swagger 2.0 API
9− specifications as well as manipulating them.
10− .
11− The original Swagger 2.0 specification is available at http://swagger.io/specification/.
12−
13−homepage: https://github.com/GetShopTV/swagger2
14−bug-reports: https://github.com/GetShopTV/swagger2/issues
15−license: BSD3
16−license-file: LICENSE
17−author: Nickolay Kudasov
18−maintainer: nickolay@getshoptv.com
19−copyright: (c) 2015-2016, GetShopTV
20−build-type: Custom
21−extra-source-files:
22− README.md
23− , CHANGELOG.md
24− , examples/*.hs
25− , include/overlapping-compat.h
26−tested-with:
27− GHC ==7.8.4
28− || ==7.10.3
29− || ==8.0.2
30− || ==8.2.2
31− || ==8.4.4
32− || ==8.6.2
33−
34−custom-setup
35− setup-depends:
36− base, Cabal, cabal-doctest >=1.0.6 && <1.1
37−
38−library
39− hs-source-dirs: src
40− include-dirs: include
41− exposed-modules:
42− Data.Swagger
43− Data.Swagger.Declare
44− Data.Swagger.Lens
45− Data.Swagger.Operation
46− Data.Swagger.ParamSchema
47− Data.Swagger.Schema
48− Data.Swagger.Schema.Generator
49− Data.Swagger.Schema.Validation
50− Data.Swagger.SchemaOptions
51−
52− -- internal modules
53− Data.Swagger.Internal
54− Data.Swagger.Internal.Schema
55− Data.Swagger.Internal.Schema.Validation
56− Data.Swagger.Internal.ParamSchema
57− Data.Swagger.Internal.Utils
58− Data.Swagger.Internal.AesonUtils
59− Data.Swagger.Internal.TypeShape
60−
61− -- GHC boot libraries
62− build-depends:
63− base >=4.7 && <4.13
64− , bytestring >=0.10.4.0 && <0.11
65− , containers >=0.5.5.1 && <0.7
66− , template-haskell >=2.9.0.0 && <2.15
67− , time >=1.4.2 && <1.9
68− , transformers >=0.3.0.0 && <0.6
69−
70− build-depends:
71− mtl >=2.1 && <2.3
72− , text >=1.2.3.0 && <1.3
73−
74− -- other dependencies
75− build-depends:
76− base-compat-batteries >=0.10.4 && <0.11
77− , aeson >=1.4.2.0 && <1.5
78− -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint
79− , cookie >=0.4.3 && <0.5
80− , generics-sop >=0.3.2.0 && <0.5
81− , hashable >=1.2.7.0 && <1.3
82− , http-media >=0.7.1.2 && <0.8
83− , insert-ordered-containers >=0.2.1.0 && <0.3
84− , lens >=4.16.1 && <4.18
85− , network >=2.6.3.5 && <3.1
86− , scientific >=0.3.6.2 && <0.4
87− , transformers-compat >=0.3 && <0.7
88− , unordered-containers >=0.2.9.0 && <0.3
89− , uuid-types >=1.0.3 && <1.1
90− , vector >=0.12.0.1 && <0.13
91− , QuickCheck >=2.10.1 && <2.13
92−
93− default-language: Haskell2010
94−
95−test-suite spec
96− type: exitcode-stdio-1.0
97− hs-source-dirs: test
98− main-is: Spec.hs
99−
100− -- From library parat
101− -- We need aeson's toEncoding for doctests too
102− build-depends:
103− base
104− , swagger2
105− , aeson
106− , base-compat-batteries
107− , bytestring
108− , containers
109− , hashable
110− , insert-ordered-containers
111− , lens
112− , mtl
113− , template-haskell
114− , text
115− , time
116− , unordered-containers
117− , vector
118−
119− -- test-suite only dependencies
120− build-depends:
121− hspec >=2.5.5 && <2.8
122− , HUnit >=1.6.0.0 && <1.7
123− , QuickCheck >=2.11.3 && <2.13
124− , quickcheck-instances >=0.3.19 && <0.14
125− , utf8-string >=1.0.1.1 && <1.1
126−
127− -- https://github.com/haskell/cabal/issues/3708
128− build-tool-depends:
129− hspec-discover:hspec-discover >=2.5.5 && <2.8
130−
131− other-modules:
132− SpecCommon
133− Data.SwaggerSpec
134− Data.Swagger.CommonTestTypes
135− Data.Swagger.ParamSchemaSpec
136− Data.Swagger.SchemaSpec
137− Data.Swagger.Schema.ValidationSpec
138− Data.Swagger.Schema.GeneratorSpec
139− default-language: Haskell2010
140−
141−test-suite doctests
142− -- See QuickCheck note in https://github.com/phadej/cabal-doctest#notes
143− build-depends: base, doctest, Glob, QuickCheck
144− default-language: Haskell2010
145− hs-source-dirs: test
146− main-is: doctests.hs
147− type: exitcode-stdio-1.0
148−
149−source-repository head
150− type: git
151− location: https://github.com/GetShopTV/swagger2.git
test/Data/Swagger/CommonTestTypes.hs → test/Data/OpenApi/CommonTestTypes.hsrenamed+492−102
@@ -2,7 +2,7 @@
22{-# LANGUAGE QuasiQuotes #-}
33{-# LANGUAGE ScopedTypeVariables #-}
44
5−module Data.Swagger.CommonTestTypes where
5+module Data.OpenApi.CommonTestTypes where
66
77import Prelude ()
88import Prelude.Compat
@@ -15,11 +15,10 @@ import Data.Map (Map)
1515import Data.Proxy
1616import Data.Set (Set)
1717import qualified Data.Text as Text
18+import Data.Word
1819import GHC.Generics
1920
20−import Data.Swagger
21−import Data.Swagger.Declare
22−import Data.Swagger.Internal (SwaggerKind (..))
21+import Data.OpenApi
2322
2423-- ========================================================================
2524-- Unit type
@@ -89,7 +88,7 @@ paintSchemaJSON = [aesonQQ|
8988 {
9089 "color":
9190 {
92− "$ref": "#/definitions/Color"
91+ "$ref": "#/components/schemas/Color"
9392 }
9493 },
9594 "required": ["color"]
@@ -182,7 +181,7 @@ userGroupSchemaJSON :: Value
182181userGroupSchemaJSON = [aesonQQ|
183182{
184183 "type": "array",
185− "items": { "$ref": "#/definitions/UserId" },
184+ "items": { "$ref": "#/components/schemas/UserId" },
186185 "uniqueItems": true
187186}
188187|]
@@ -229,7 +228,7 @@ playerSchemaJSON = [aesonQQ|
229228 {
230229 "position":
231230 {
232− "$ref": "#/definitions/Point"
231+ "$ref": "#/components/schemas/Point"
233232 }
234233 },
235234 "required": ["position"]
@@ -251,7 +250,7 @@ playersSchemaJSON = [aesonQQ|
251250 {
252251 "position":
253252 {
254− "$ref": "#/definitions/Point"
253+ "$ref": "#/components/schemas/Point"
255254 }
256255 },
257256 "required": ["position"]
@@ -272,106 +271,192 @@ instance ToSchema Character
272271characterSchemaJSON :: Value
273272characterSchemaJSON = [aesonQQ|
274273{
275− "type": "object",
276− "properties":
274+ "oneOf": [
277275 {
278− "PC": { "$ref": "#/definitions/Player" },
279− "NPC":
280− {
281− "type": "object",
282− "properties":
283− {
284− "npcName": { "type": "string" },
285− "npcPosition": { "$ref": "#/definitions/Point" }
286− },
287− "required": ["npcName", "npcPosition"]
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"
288290 }
291+ }
289292 },
290− "maxProperties": 1,
291− "minProperties": 1
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"
292317}
318+
293319|]
294320
295321characterInlinedSchemaJSON :: Value
296322characterInlinedSchemaJSON = [aesonQQ|
297323{
298− "type": "object",
299− "properties":
324+ "oneOf": [
300325 {
301− "PC":
302− {
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+ ],
303342 "type": "object",
304− "properties":
305− {
306− "position":
307− {
308− "type": "object",
309− "properties":
310− {
311− "x": { "type": "number", "format": "double" },
312− "y": { "type": "number", "format": "double" }
313− },
314− "required": ["x", "y"]
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"
315358 }
316− },
317− "required": ["position"]
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+ ]
318378 },
319− "NPC":
320− {
379+ "npcPosition": {
380+ "required": [
381+ "x",
382+ "y"
383+ ],
321384 "type": "object",
322− "properties":
323− {
324− "npcName": { "type": "string" },
325− "npcPosition":
326− {
327− "type": "object",
328− "properties":
329− {
330− "x": { "type": "number", "format": "double" },
331− "y": { "type": "number", "format": "double" }
332− },
333− "required": ["x", "y"]
334− }
385+ "properties": {
386+ "x": {
387+ "format": "double",
388+ "type": "number"
335389 },
336− "required": ["npcName", "npcPosition"]
390+ "y": {
391+ "format": "double",
392+ "type": "number"
393+ }
394+ }
395+ },
396+ "npcName": {
397+ "type": "string"
337398 }
338− },
339− "maxProperties": 1,
340− "minProperties": 1
399+ }
400+ }
401+ ],
402+ "type": "object"
341403}
342404|]
343405
344406characterInlinedPlayerSchemaJSON :: Value
345407characterInlinedPlayerSchemaJSON = [aesonQQ|
346408{
347− "type": "object",
348− "properties":
409+ "oneOf": [
349410 {
350− "PC":
351− {
352− "type": "object",
353− "properties":
354− {
355− "position":
356− {
357− "$ref": "#/definitions/Point"
358− }
359− },
360− "required": ["position"]
411+ "required": [
412+ "tag",
413+ "contents"
414+ ],
415+ "type": "object",
416+ "properties": {
417+ "tag": {
418+ "type": "string",
419+ "enum": [
420+ "PC"
421+ ]
361422 },
362− "NPC":
363− {
423+ "contents": {
424+ "required": [
425+ "position"
426+ ],
364427 "type": "object",
365− "properties":
366− {
367− "npcName": { "type": "string" },
368− "npcPosition": { "$ref": "#/definitions/Point" }
369− },
370− "required": ["npcName", "npcPosition"]
428+ "properties": {
429+ "position": {
430+ "$ref": "#/components/schemas/Point"
431+ }
432+ }
371433 }
434+ }
372435 },
373− "maxProperties": 1,
374− "minProperties": 1
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"
375460}
376461|]
377462
@@ -483,7 +568,7 @@ myRoseTreeSchemaJSON = [aesonQQ|
483568 "type": "array",
484569 "items":
485570 {
486− "$ref": "#/definitions/RoseTree"
571+ "$ref": "#/components/schemas/RoseTree"
487572 }
488573 }
489574 },
@@ -512,7 +597,7 @@ myRoseTreeSchemaJSON' = [aesonQQ|
512597 "type": "array",
513598 "items":
514599 {
515− "$ref": "#/definitions/myrosetree'"
600+ "$ref": "#/components/schemas/myrosetree'"
516601 }
517602 }
518603 },
@@ -543,42 +628,169 @@ data Light
543628 deriving (Generic)
544629
545630instance ToSchema Light where
546− declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
631+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
547632 { unwrapUnaryRecords = True }
548633
549634lightSchemaJSON :: Value
550635lightSchemaJSON = [aesonQQ|
551636{
552− "type": "object",
553− "properties":
637+ "oneOf": [
554638 {
555− "NoLight": { "type": "array", "items": {}, "maxItems": 0, "example": [] },
556− "LightFreq": { "type": "number", "format": "double" },
557− "LightColor": { "$ref": "#/definitions/Color" },
558− "LightWaveLength": { "type": "number", "format": "double" }
639+ "required": [
640+ "tag"
641+ ],
642+ "type": "object",
643+ "properties": {
644+ "tag": {
645+ "type": "string",
646+ "enum": [
647+ "NoLight"
648+ ]
649+ }
650+ }
559651 },
560− "maxProperties": 1,
561− "minProperties": 1
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+ },
671+ {
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+ }
688+ },
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"
562710}
563711|]
564712
565713lightInlinedSchemaJSON :: Value
566714lightInlinedSchemaJSON = [aesonQQ|
567715{
568− "type": "object",
569− "properties":
716+ "oneOf": [
570717 {
571− "NoLight": { "type": "array", "items": {}, "maxItems": 0, "example": [] },
572− "LightFreq": { "type": "number", "format": "double" },
573− "LightColor":
574− {
718+ "required": [
719+ "tag"
720+ ],
721+ "type": "object",
722+ "properties": {
723+ "tag": {
575724 "type": "string",
576− "enum": ["Red", "Green", "Blue"]
725+ "enum": [
726+ "NoLight"
727+ ]
728+ }
729+ }
730+ },
731+ {
732+ "required": [
733+ "tag",
734+ "contents"
735+ ],
736+ "type": "object",
737+ "properties": {
738+ "tag": {
739+ "type": "string",
740+ "enum": [
741+ "LightFreq"
742+ ]
577743 },
578− "LightWaveLength": { "type": "number", "format": "double" }
744+ "contents": {
745+ "format": "double",
746+ "type": "number"
747+ }
748+ }
579749 },
580− "maxProperties": 1,
581− "minProperties": 1
750+ {
751+ "required": [
752+ "tag",
753+ "contents"
754+ ],
755+ "type": "object",
756+ "properties": {
757+ "tag": {
758+ "type": "string",
759+ "enum": [
760+ "LightColor"
761+ ]
762+ },
763+ "contents": {
764+ "type": "string",
765+ "enum": [
766+ "Red",
767+ "Green",
768+ "Blue"
769+ ]
770+ }
771+ }
772+ },
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"
582794}
583795|]
584796
@@ -650,3 +862,181 @@ singleMaybeFieldSchemaJSON = [aesonQQ|
650862 }
651863}
652864|]
865+
866+-- ========================================================================
867+-- Natural Language (single field data with recursive fields)
868+-- ========================================================================
869+
870+data Predicate
871+ = PredicateNoun Noun
872+ | PredicateOmitted Omitted
873+ deriving (Eq, Ord, Read, Show, Generic)
874+instance ToJSON Predicate
875+instance ToSchema Predicate
876+
877+data Noun
878+ = Noun
879+ { nounSurf :: LangWord
880+ , nounModify :: [Modifier]
881+ }
882+ deriving (Eq, Ord, Read, Show, Generic)
883+instance ToJSON Noun
884+instance ToSchema Noun
885+
886+data LangWord
887+ = LangWord
888+ { langWordSurf :: String
889+ , langWordBase :: String
890+ }
891+ deriving (Eq, Ord, Read, Show, Generic)
892+instance ToJSON LangWord
893+instance ToSchema LangWord
894+
895+data Modifier
896+ = ModifierNoun Noun
897+ | ModifierOmitted Omitted
898+ deriving (Eq, Ord, Read, Show, Generic)
899+instance ToJSON Modifier
900+instance ToSchema Modifier
901+
902+newtype Omitted
903+ = Omitted
904+ { omittedModify :: [Modifier]
905+ }
906+ deriving (Eq, Ord, Read, Show, Generic)
907+instance ToJSON Omitted
908+instance ToSchema Omitted
909+
910+predicateSchemaDeclareJSON :: Value
911+predicateSchemaDeclareJSON = [aesonQQ|
912+[
913+ {
914+ "Predicate": {
915+ "oneOf": [
916+ {
917+ "properties": {
918+ "contents": { "$ref": "#/components/schemas/Noun" },
919+ "tag": { "enum": ["PredicateNoun"], "type": "string" }
920+ },
921+ "required": ["tag", "contents"],
922+ "type": "object"
923+ },
924+ {
925+ "properties": {
926+ "contents": { "$ref": "#/components/schemas/Omitted" },
927+ "tag": { "enum": ["PredicateOmitted"], "type": "string" }
928+ },
929+ "required": ["tag", "contents"],
930+ "type": "object"
931+ }
932+ ],
933+ "type": "object"
934+ },
935+ "Noun": {
936+ "properties": {
937+ "nounModify": {
938+ "items": { "$ref": "#/components/schemas/Modifier" },
939+ "type": "array"
940+ },
941+ "nounSurf": { "$ref": "#/components/schemas/LangWord" }
942+ },
943+ "required": ["nounSurf", "nounModify"],
944+ "type": "object"
945+ },
946+ "LangWord": {
947+ "properties": {
948+ "langWordBase": { "type": "string" },
949+ "langWordSurf": { "type": "string" }
950+ },
951+ "required": ["langWordSurf", "langWordBase"],
952+ "type": "object"
953+ },
954+ "Modifier": {
955+ "oneOf": [
956+ {
957+ "properties": {
958+ "contents": { "$ref": "#/components/schemas/Noun" },
959+ "tag": { "enum": ["ModifierNoun"], "type": "string" }
960+ },
961+ "required": ["tag", "contents"],
962+ "type": "object"
963+ },
964+ {
965+ "properties": {
966+ "contents": { "$ref": "#/components/schemas/Omitted" },
967+ "tag": { "enum": ["ModifierOmitted"], "type": "string" }
968+ },
969+ "required": ["tag", "contents"],
970+ "type": "object"
971+ }
972+ ],
973+ "type": "object"
974+ },
975+ "Omitted": {
976+ "properties": {
977+ "omittedModify": {
978+ "items": { "$ref": "#/components/schemas/Modifier" },
979+ "type": "array"
980+ }
981+ },
982+ "required": ["omittedModify"],
983+ "type": "object"
984+ }
985+ },
986+ { "$ref": "#/components/schemas/Predicate" }
987+]
988+|]
989+
990+-- ========================================================================
991+-- TimeOfDay
992+-- ========================================================================
993+data TimeOfDay
994+ = Int
995+ | Pico
996+ deriving (Generic)
997+instance ToSchema TimeOfDay
998+instance ToParamSchema TimeOfDay
999+
1000+
1001+timeOfDaySchemaJSON :: Value
1002+timeOfDaySchemaJSON = [aesonQQ|
1003+{
1004+ "example": "12:33:15",
1005+ "type": "string",
1006+ "format": "hh:MM:ss"
1007+}
1008+|]
1009+
1010+timeOfDayParamSchemaJSON :: Value
1011+timeOfDayParamSchemaJSON = [aesonQQ|
1012+{
1013+ "type": "string",
1014+ "format": "hh:MM:ss"
1015+}
1016+|]
1017+
1018+
1019+-- ========================================================================
1020+-- UnsignedInts
1021+-- ========================================================================
1022+data UnsignedInts = UnsignedInts
1023+ { unsignedIntsUint32 :: Word32
1024+ , unsignedIntsUint64 :: Word64
1025+ } deriving (Generic)
1026+
1027+instance ToSchema UnsignedInts where
1028+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions
1029+ { fieldLabelModifier = map toLower . drop (length "unsignedInts") }
1030+
1031+unsignedIntsSchemaJSON :: Value
1032+unsignedIntsSchemaJSON = [aesonQQ|
1033+{
1034+ "type": "object",
1035+ "properties":
1036+ {
1037+ "uint32": { "type": "integer", "format": "int32", "minimum": 0, "maximum": 4294967295 },
1038+ "uint64": { "type": "integer", "format": "int64", "minimum": 0, "maximum": 18446744073709551615 }
1039+ },
1040+ "required": ["uint32", "uint64"]
1041+}
1042+|]
test/Data/Swagger/ParamSchemaSpec.hs → test/Data/OpenApi/ParamSchemaSpec.hsrenamed+10−6
@@ -3,23 +3,26 @@
33{-# LANGUAGE ScopedTypeVariables #-}
44{-# LANGUAGE TemplateHaskell #-}
55{-# LANGUAGE QuasiQuotes #-}
6−module Data.Swagger.ParamSchemaSpec where
6+{-# LANGUAGE OverloadedStrings #-}
7+module Data.OpenApi.ParamSchemaSpec where
78
8−import Data.Aeson (Value)
9+import Data.Aeson
910import Data.Aeson.QQ.Simple
1011import Data.Char
1112import Data.Proxy
1213import GHC.Generics
1314
14−import Data.Swagger
15−import Data.Swagger.Internal (SwaggerKind(..))
15+import Data.OpenApi
1616
17−import Data.Swagger.CommonTestTypes
17+import Data.OpenApi.CommonTestTypes
1818import SpecCommon
1919import Test.Hspec
20+import Data.Time.LocalTime
21+
22+import qualified Data.HashMap.Strict as HM
2023
2124checkToParamSchema :: ToParamSchema a => Proxy a -> Value -> Spec
22−checkToParamSchema proxy js = (toParamSchema proxy :: ParamSchema ('SwaggerKindNormal Param)) <=> js
25+checkToParamSchema proxy js = (toParamSchema proxy :: Schema) <=> js
2326
2427spec :: Spec
2528spec = do
@@ -30,6 +33,7 @@ spec = do
3033 context "Unary records" $ do
3134 context "Email (unary record)" $ checkToParamSchema (Proxy :: Proxy Email) emailSchemaJSON
3235 context "UserId (non-record newtype)" $ checkToParamSchema (Proxy :: Proxy UserId) userIdSchemaJSON
36+ context "TimeOfDay" $ checkToParamSchema (Proxy :: Proxy Data.Time.LocalTime.TimeOfDay) timeOfDayParamSchemaJSON
3337
3438main :: IO ()
3539main = hspec spec
test/Data/Swagger/Schema/GeneratorSpec.hs → test/Data/OpenApi/Schema/GeneratorSpec.hsrenamed+7−7
@@ -1,13 +1,13 @@
11{-# LANGUAGE OverloadedLists #-}
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE PackageImports #-}
4−module Data.Swagger.Schema.GeneratorSpec where
4+module Data.OpenApi.Schema.GeneratorSpec where
55
66import Prelude ()
77import Prelude.Compat
88
9−import Data.Swagger
10−import Data.Swagger.Schema.Generator
9+import Data.OpenApi
10+import Data.OpenApi.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_ .~ SwaggerObject
106+ & type_ ?~ OpenApiObject
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_ .~ SwaggerObject
126+ & type_ ?~ OpenApiObject
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_ .~ SwaggerObject
148+ & type_ ?~ OpenApiObject
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_ .~ SwaggerObject
166+ & type_ ?~ OpenApiObject
167167 & properties .~ [("propE", boolSchema)]
168168 & required .~ ["propE"]
test/Data/Swagger/Schema/ValidationSpec.hs → test/Data/OpenApi/Schema/ValidationSpec.hsrenamed+55−9
@@ -4,11 +4,15 @@
44{-# LANGUAGE RecordWildCards #-}
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# OPTIONS_GHC -fno-warn-orphans #-}
7−module Data.Swagger.Schema.ValidationSpec where
7+module Data.OpenApi.Schema.ValidationSpec where
88
99import Control.Applicative
1010import Control.Lens ((&), (.~), (?~))
1111import 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
1216import Data.Aeson.Types
1317import Data.Hashable (Hashable)
1418import Data.HashMap.Strict (HashMap)
@@ -29,8 +33,9 @@ import Data.Version (Version)
2933import Data.Word
3034import GHC.Generics
3135
32−import Data.Swagger
33−import Data.Swagger.Declare
36+import Data.OpenApi
37+import Data.OpenApi.Declare
38+import Data.OpenApi.Aeson.Compat (stringToKey)
3439
3540import Test.Hspec
3641import Test.Hspec.QuickCheck
@@ -92,6 +97,8 @@ spec = do
9297 prop "Paint" $ shouldValidate (Proxy :: Proxy Paint)
9398 prop "MyRoseTree" $ shouldValidate (Proxy :: Proxy MyRoseTree)
9499 prop "Light" $ shouldValidate (Proxy :: Proxy Light)
100+ prop "Light TaggedObject" $ shouldValidate (Proxy :: Proxy LightTaggedObject)
101+ prop "Light UntaggedValue" $ shouldValidate (Proxy :: Proxy LightUntaggedValue)
95102 prop "ButtonImages" $ shouldValidate (Proxy :: Proxy ButtonImages)
96103 prop "Version" $ shouldValidate (Proxy :: Proxy Version)
97104 prop "FreeForm" $ shouldValidate (Proxy :: Proxy FreeForm)
@@ -123,9 +130,9 @@ instance Arbitrary Person where
123130
124131invalidPersonToJSON :: Person -> Value
125132invalidPersonToJSON Person{..} = object
126− [ T.pack "personName" .= toJSON name
127− , T.pack "personPhone" .= toJSON phone
128− , T.pack "personEmail" .= toJSON email
133+ [ stringToKey "personName" .= toJSON name
134+ , stringToKey "personPhone" .= toJSON phone
135+ , stringToKey "personEmail" .= toJSON email
129136 ]
130137
131138-- ========================================================================
@@ -189,10 +196,10 @@ instance Arbitrary MyRoseTree where
189196data Light = NoLight | LightFreq Double | LightColor Color deriving (Show, Generic)
190197
191198instance ToSchema Light where
192− declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
199+ declareNamedSchema = genericDeclareNamedSchema defaultSchemaOptions { Data.OpenApi.sumEncoding = ObjectWithSingleField }
193200
194201instance ToJSON Light where
195− toJSON = genericToJSON defaultOptions { sumEncoding = ObjectWithSingleField }
202+ toJSON = genericToJSON defaultOptions { Data.Aeson.Types.sumEncoding = ObjectWithSingleField }
196203
197204instance Arbitrary Light where
198205 arbitrary = oneof
@@ -204,6 +211,34 @@ instance Arbitrary Light where
204211invalidLightToJSON :: Light -> Value
205212invalidLightToJSON = genericToJSON defaultOptions
206213
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+
207242-- ========================================================================
208243-- ButtonImages (bounded enum key mapping)
209244-- ========================================================================
@@ -248,7 +283,7 @@ instance ToJSON FreeForm where
248283
249284instance ToSchema FreeForm where
250285 declareNamedSchema _ = pure $ NamedSchema (Just $ T.pack "FreeForm") $ mempty
251− & type_ .~ SwaggerObject
286+ & type_ ?~ OpenApiObject
252287 & additionalProperties ?~ AdditionalPropertiesAllowed True
253288
254289instance Arbitrary FreeForm where
@@ -266,6 +301,8 @@ instance Eq ZonedTime where
266301-- Arbitrary instance for Data.Aeson.Value
267302-- ========================================================================
268303
304+-- These instances were introduces in aeson upstream in 2.0.3
305+#if !MIN_VERSION_aeson(2,0,3)
269306instance Arbitrary Value where
270307 -- Weights are almost random
271308 -- Uniform oneof tends not to build complex objects cause of recursive call.
@@ -276,3 +313,12 @@ instance Arbitrary Value where
276313 , (3, Number <$> arbitrary)
277314 , (3, Bool <$> arbitrary)
278315 , (1, return Null) ]
316+
317+#if MIN_VERSION_aeson(2,0,0)
318+instance Arbitrary v => Arbitrary (KeyMap.KeyMap v) where
319+ arbitrary = KeyMap.fromList <$> arbitrary
320+
321+instance Arbitrary Key.Key where
322+ arbitrary = Key.fromText <$> arbitrary
323+#endif
324+#endif
test/Data/Swagger/SchemaSpec.hs → test/Data/OpenApi/SchemaSpec.hsrenamed+21−17
@@ -2,65 +2,66 @@
22{-# LANGUAGE ScopedTypeVariables #-}
33{-# LANGUAGE TemplateHaskell #-}
44{-# LANGUAGE QuasiQuotes #-}
5−module Data.Swagger.SchemaSpec where
5+module Data.OpenApi.SchemaSpec where
66
77import Prelude ()
88import Prelude.Compat
99
1010import Control.Lens ((^.))
11−import Data.Aeson (Value, ToJSON(..), ToJSONKey(..))
12−import Data.Aeson.Types (toJSONKeyText)
13−import Data.Aeson.QQ.Simple
14−import Data.Char
11+import Data.Aeson (Value)
1512import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
1613import Data.Proxy
1714import Data.Set (Set)
18−import Data.Map (Map)
1915import qualified Data.Text as Text
20−import GHC.Generics
2116
22−import Data.Swagger
23−import Data.Swagger.Declare
17+import Data.OpenApi
18+import Data.OpenApi.Declare
2419
25−import Data.Swagger.CommonTestTypes
20+import Data.OpenApi.CommonTestTypes
2621import SpecCommon
2722import Test.Hspec
2823
29−checkToSchema :: ToSchema a => Proxy a -> Value -> Spec
24+import qualified Data.HashMap.Strict as HM
25+import Data.Time.LocalTime
26+
27+checkToSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
3028checkToSchema proxy js = toSchema proxy <=> js
3129
32−checkSchemaName :: ToSchema a => Maybe String -> Proxy a -> Spec
30+checkSchemaName :: (HasCallStack, ToSchema a) => Maybe String -> Proxy a -> Spec
3331checkSchemaName sname proxy =
3432 it ("schema name is " ++ show sname) $
3533 schemaName proxy `shouldBe` fmap Text.pack sname
3634
37−checkDefs :: ToSchema a => Proxy a -> [String] -> Spec
35+checkDefs :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
3836checkDefs proxy names =
3937 it ("uses these definitions " ++ show names) $
4038 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
4139 where
4240 defs = execDeclare (declareNamedSchema proxy) mempty
4341
44−checkProperties :: ToSchema a => Proxy a -> [String] -> Spec
42+checkProperties :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
4543checkProperties proxy names =
4644 it ("has these fields in order " ++ show names) $
4745 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
4846 where
4947 fields = toSchema proxy ^. properties
5048
51−checkInlinedSchema :: ToSchema a => Proxy a -> Value -> Spec
49+checkInlinedSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
5250checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
5351
54−checkInlinedSchemas :: ToSchema a => [String] -> Proxy a -> Value -> Spec
52+checkInlinedSchemas :: (HasCallStack, ToSchema a) => [String] -> Proxy a -> Value -> Spec
5553checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
5654 where
5755 (defs, s) = runDeclare (declareSchema proxy) mempty
5856
59−checkInlinedRecSchema :: ToSchema a => Proxy a -> Value -> Spec
57+checkInlinedRecSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
6058checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
6159 where
6260 (defs, s) = runDeclare (declareSchema proxy) mempty
6361
62+checkToSchemaDeclare :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
63+checkToSchemaDeclare proxy js = runDeclare (declareSchemaRef proxy) mempty <=> js
64+
6465spec :: Spec
6566spec = do
6667 describe "Generic ToSchema" $ do
@@ -80,6 +81,7 @@ spec = do
8081 context "UserId (non-record newtype)" $ checkToSchema (Proxy :: Proxy UserId) userIdSchemaJSON
8182 context "Player (unary record)" $ checkToSchema (Proxy :: Proxy Player) playerSchemaJSON
8283 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
8385 context "Players (inlining schema)" $ checkToSchema (Proxy :: Proxy Players) playersSchemaJSON
8486 context "MyRoseTree (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree) myRoseTreeSchemaJSON
8587 context "MyRoseTree' (datatypeNameModifier)" $ checkToSchema (Proxy :: Proxy MyRoseTree') myRoseTreeSchemaJSON'
@@ -87,6 +89,7 @@ spec = do
8789 context "Status (sum of unary constructors)" $ checkToSchema (Proxy :: Proxy Status) statusSchemaJSON
8890 context "Character (ref and record sum)" $ checkToSchema (Proxy :: Proxy Character) characterSchemaJSON
8991 context "Light (sum with unwrapUnaryRecords)" $ checkToSchema (Proxy :: Proxy Light) lightSchemaJSON
92+ context "UnsignedInts" $ checkToSchema (Proxy :: Proxy UnsignedInts) unsignedIntsSchemaJSON
9093 context "Schema name" $ do
9194 context "String" $ checkSchemaName Nothing (Proxy :: Proxy String)
9295 context "(Int, Float)" $ checkSchemaName Nothing (Proxy :: Proxy (Int, Float))
@@ -110,6 +113,7 @@ spec = do
110113 context "MyRoseTree' (inlineNonRecursiveSchemas)" $ checkInlinedRecSchema (Proxy :: Proxy MyRoseTree') myRoseTreeSchemaJSON'
111114 describe "Bounded Enum key mapping" $ do
112115 context "ButtonImages" $ checkToSchema (Proxy :: Proxy ButtonImages) buttonImagesSchemaJSON
116+ context "TimeOfDay" $ checkToSchema (Proxy :: Proxy Data.Time.LocalTime.TimeOfDay) timeOfDaySchemaJSON
113117
114118main :: IO ()
115119main = hspec spec
test/Data/OpenApiSpec.hsnew file+962
@@ -0,0 +1,962 @@
1+{-# LANGUAGE OverloadedLists #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{-# LANGUAGE QuasiQuotes #-}
5+module Data.OpenApiSpec where
6+
7+import Prelude ()
8+import Prelude.Compat
9+
10+import Control.Lens
11+
12+import Data.Aeson
13+import Data.Aeson.QQ.Simple
14+import Data.HashMap.Strict (HashMap)
15+import qualified Data.HashSet.InsOrd as InsOrdHS
16+import Data.Text (Text)
17+
18+import Data.OpenApi
19+import SpecCommon
20+import Test.Hspec hiding (example)
21+
22+spec :: Spec
23+spec = do
24+ describe "License Object" $ licenseExample <=> licenseExampleJSON
25+ describe "Contact Object" $ contactExample <=> contactExampleJSON
26+ describe "Info Object" $ infoExample <=> infoExampleJSON
27+ describe "Operation Object" $ operationExample <=> operationExampleJSON
28+ describe "Schema Object" $ do
29+ context "Primitive Sample" $ schemaPrimitiveExample <=> schemaPrimitiveExampleJSON
30+ context "Simple Model" $ schemaSimpleModelExample <=> schemaSimpleModelExampleJSON
31+ context "Model with Map/Dictionary Properties" $ schemaModelDictExample <=> schemaModelDictExampleJSON
32+ context "Model with Arbitrary Properties" $ schemaAdditionalExample <=> schemaAdditionalExampleJSON
33+ context "Model with Example" $ schemaWithExampleExample <=> schemaWithExampleExampleJSON
34+ describe "Definitions Object" $ definitionsExample <=> definitionsExampleJSON
35+ describe "Parameters Definition Object" $ paramsDefinitionExample <=> paramsDefinitionExampleJSON
36+ describe "Responses Definition Object" $ responsesDefinitionExample <=> responsesDefinitionExampleJSON
37+ describe "Security Definitions Object" $ securityDefinitionsExample <=> securityDefinitionsExampleJSON
38+ describe "OAuth2 Security Definitions with merged Scope" $ oAuth2SecurityDefinitionsExample <=> oAuth2SecurityDefinitionsExampleJSON
39+ describe "Composition Schema Example" $ compositionSchemaExample <=> compositionSchemaExampleJSON
40+ describe "Swagger Object" $ do
41+ context "Example with no paths" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
42+ context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
43+ context "PetStore Example" $ do
44+ it "decodes successfully" $ do
45+ fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: OpenApi) -> True; _ -> False)
46+ it "roundtrips: fmap toJSON . fromJSON" $ do
47+ (toJSON :: OpenApi -> Value) <$> fromJSON petstoreExampleJSON `shouldBe` Success petstoreExampleJSON
48+ context "Security schemes" $ do
49+ it "merged correctly" $ do
50+ let merged = oAuth2SecurityDefinitionsReadOpenApi <> oAuth2SecurityDefinitionsWriteOpenApi
51+ merged `shouldBe` oAuth2SecurityDefinitionsOpenApi
52+
53+main :: IO ()
54+main = hspec spec
55+
56+-- =======================================================================
57+-- Info object
58+-- =======================================================================
59+
60+infoExample :: Info
61+infoExample = mempty
62+ & title .~ "Swagger Sample App"
63+ & description ?~ "This is a sample server Petstore server."
64+ & termsOfService ?~ "http://swagger.io/terms/"
65+ & contact ?~ contactExample
66+ & license ?~ licenseExample
67+ & version .~ "1.0.1"
68+
69+infoExampleJSON :: Value
70+infoExampleJSON = [aesonQQ|
71+{
72+ "title": "Swagger Sample App",
73+ "description": "This is a sample server Petstore server.",
74+ "termsOfService": "http://swagger.io/terms/",
75+ "contact": {
76+ "name": "API Support",
77+ "url": "http://www.swagger.io/support",
78+ "email": "support@swagger.io"
79+ },
80+ "license": {
81+ "name": "Apache 2.0",
82+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
83+ },
84+ "version": "1.0.1"
85+}
86+|]
87+
88+-- =======================================================================
89+-- Contact object
90+-- =======================================================================
91+
92+contactExample :: Contact
93+contactExample = mempty
94+ & name ?~ "API Support"
95+ & url ?~ URL "http://www.swagger.io/support"
96+ & email ?~ "support@swagger.io"
97+
98+contactExampleJSON :: Value
99+contactExampleJSON = [aesonQQ|
100+{
101+ "name": "API Support",
102+ "url": "http://www.swagger.io/support",
103+ "email": "support@swagger.io"
104+}
105+|]
106+
107+-- =======================================================================
108+-- License object
109+-- =======================================================================
110+
111+licenseExample :: License
112+licenseExample = "Apache 2.0"
113+ & url ?~ URL "http://www.apache.org/licenses/LICENSE-2.0.html"
114+
115+licenseExampleJSON :: Value
116+licenseExampleJSON = [aesonQQ|
117+{
118+ "name": "Apache 2.0",
119+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
120+}
121+|]
122+
123+
124+-- =======================================================================
125+-- Operation object
126+-- =======================================================================
127+
128+operationExample :: Operation
129+operationExample = mempty
130+ & tags .~ InsOrdHS.fromList ["pet"]
131+ & summary ?~ "Updates a pet in the store with form data"
132+ & description ?~ ""
133+ & operationId ?~ "updatePetWithForm"
134+ & parameters .~ [Inline (mempty
135+ & name .~ "petId"
136+ & description ?~ "ID of pet that needs to be updated"
137+ & required ?~ True
138+ & in_ .~ ParamPath
139+ & schema ?~ Inline (mempty & type_ ?~ OpenApiString))]
140+ & requestBody ?~ Inline (
141+ mempty & content . at "application/x-www-form-urlencoded" ?~ (mempty & schema ?~ (Inline (mempty
142+ & properties . at "petId" ?~ Inline (mempty
143+ & description ?~ "Updated name of the pet"
144+ & type_ ?~ OpenApiString)
145+ & properties . at "status" ?~ Inline (mempty
146+ & description ?~ "Updated status of the pet"
147+ & type_ ?~ OpenApiString)))))
148+ & at 200 ?~ "Pet updated."
149+ & at 405 ?~ "Invalid input"
150+ & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
151+
152+operationExampleJSON :: Value
153+operationExampleJSON = [aesonQQ|
154+{
155+ "tags": [
156+ "pet"
157+ ],
158+ "summary": "Updates a pet in the store with form data",
159+ "description": "",
160+ "operationId": "updatePetWithForm",
161+ "parameters": [
162+ {
163+ "required": true,
164+ "schema": {
165+ "type": "string"
166+ },
167+ "in": "path",
168+ "name": "petId",
169+ "description": "ID of pet that needs to be updated"
170+ }
171+ ],
172+ "requestBody": {
173+ "content": {
174+ "application/x-www-form-urlencoded": {
175+ "schema": {
176+ "properties": {
177+ "petId": {
178+ "type": "string",
179+ "description": "Updated name of the pet"
180+ },
181+ "status": {
182+ "type": "string",
183+ "description": "Updated status of the pet"
184+ }
185+ }
186+ }
187+ }
188+ }
189+ },
190+ "responses": {
191+ "200": {
192+ "description": "Pet updated."
193+ },
194+ "405": {
195+ "description": "Invalid input"
196+ }
197+ },
198+ "security": [
199+ {
200+ "petstore_auth": [
201+ "write:pets",
202+ "read:pets"
203+ ]
204+ }
205+ ]
206+}
207+|]
208+
209+-- =======================================================================
210+-- Schema object
211+-- =======================================================================
212+
213+schemaPrimitiveExample :: Schema
214+schemaPrimitiveExample = mempty
215+ & type_ ?~ OpenApiString
216+ & format ?~ "email"
217+
218+schemaPrimitiveExampleJSON :: Value
219+schemaPrimitiveExampleJSON = [aesonQQ|
220+{
221+ "type": "string",
222+ "format": "email"
223+}
224+|]
225+
226+schemaSimpleModelExample :: Schema
227+schemaSimpleModelExample = mempty
228+ & type_ ?~ OpenApiObject
229+ & required .~ [ "name" ]
230+ & properties .~
231+ [ ("name", Inline (mempty & type_ ?~ OpenApiString))
232+ , ("address", Ref (Reference "Address"))
233+ , ("age", Inline $ mempty
234+ & minimum_ ?~ 0
235+ & type_ ?~ OpenApiInteger
236+ & format ?~ "int32" ) ]
237+
238+schemaSimpleModelExampleJSON :: Value
239+schemaSimpleModelExampleJSON = [aesonQQ|
240+{ "required": [ "name" ],
241+ "properties": {
242+ "name": {
243+ "type": "string"
244+ },
245+ "address": {
246+ "$ref": "#/components/schemas/Address"
247+ },
248+ "age": {
249+ "format": "int32",
250+ "minimum": 0,
251+ "type": "integer"
252+ }
253+ },
254+ "type": "object"
255+}
256+|]
257+
258+schemaModelDictExample :: Schema
259+schemaModelDictExample = mempty
260+ & type_ ?~ OpenApiObject
261+ & additionalProperties ?~ AdditionalPropertiesSchema (Inline (mempty & type_ ?~ OpenApiString))
262+
263+schemaModelDictExampleJSON :: Value
264+schemaModelDictExampleJSON = [aesonQQ|
265+{
266+ "type": "object",
267+ "additionalProperties": {
268+ "type": "string"
269+ }
270+}
271+|]
272+
273+schemaAdditionalExample :: Schema
274+schemaAdditionalExample = mempty
275+ & type_ ?~ OpenApiObject
276+ & additionalProperties ?~ AdditionalPropertiesAllowed True
277+
278+schemaAdditionalExampleJSON :: Value
279+schemaAdditionalExampleJSON = [aesonQQ|
280+{
281+ "type": "object",
282+ "additionalProperties": true
283+}
284+|]
285+
286+schemaWithExampleExample :: Schema
287+schemaWithExampleExample = mempty
288+ & type_ ?~ OpenApiObject
289+ & properties .~
290+ [ ("id", Inline $ mempty
291+ & type_ ?~ OpenApiInteger
292+ & format ?~ "int64" )
293+ , ("name", Inline $ mempty
294+ & type_ ?~ OpenApiString) ]
295+ & required .~ [ "name" ]
296+ & example ?~ [aesonQQ|
297+ {
298+ "name": "Puma",
299+ "id": 1
300+ }
301+ |]
302+
303+schemaWithExampleExampleJSON :: Value
304+schemaWithExampleExampleJSON = [aesonQQ|
305+{
306+ "type": "object",
307+ "properties": {
308+ "id": {
309+ "type": "integer",
310+ "format": "int64"
311+ },
312+ "name": {
313+ "type": "string"
314+ }
315+ },
316+ "required": [
317+ "name"
318+ ],
319+ "example": {
320+ "name": "Puma",
321+ "id": 1
322+ }
323+}
324+|]
325+
326+-- =======================================================================
327+-- Definitions object
328+-- =======================================================================
329+
330+definitionsExample :: HashMap Text Schema
331+definitionsExample =
332+ [ ("Category", mempty
333+ & type_ ?~ OpenApiObject
334+ & properties .~
335+ [ ("id", Inline $ mempty
336+ & type_ ?~ OpenApiInteger
337+ & format ?~ "int64")
338+ , ("name", Inline (mempty & type_ ?~ OpenApiString)) ] )
339+ , ("Tag", mempty
340+ & type_ ?~ OpenApiObject
341+ & properties .~
342+ [ ("id", Inline $ mempty
343+ & type_ ?~ OpenApiInteger
344+ & format ?~ "int64")
345+ , ("name", Inline (mempty & type_ ?~ OpenApiString)) ] ) ]
346+
347+definitionsExampleJSON :: Value
348+definitionsExampleJSON = [aesonQQ|
349+{
350+ "Category": {
351+ "type": "object",
352+ "properties": {
353+ "id": {
354+ "type": "integer",
355+ "format": "int64"
356+ },
357+ "name": {
358+ "type": "string"
359+ }
360+ }
361+ },
362+ "Tag": {
363+ "type": "object",
364+ "properties": {
365+ "id": {
366+ "type": "integer",
367+ "format": "int64"
368+ },
369+ "name": {
370+ "type": "string"
371+ }
372+ }
373+ }
374+}
375+|]
376+
377+-- =======================================================================
378+-- Parameters Definition object
379+-- =======================================================================
380+
381+paramsDefinitionExample :: HashMap Text Param
382+paramsDefinitionExample =
383+ [ ("skipParam", mempty
384+ & name .~ "skip"
385+ & description ?~ "number of items to skip"
386+ & required ?~ True
387+ & in_ .~ ParamQuery
388+ & schema ?~ Inline (mempty
389+ & type_ ?~ OpenApiInteger
390+ & format ?~ "int32" ))
391+ , ("limitParam", mempty
392+ & name .~ "limit"
393+ & description ?~ "max records to return"
394+ & required ?~ True
395+ & in_ .~ ParamQuery
396+ & schema ?~ Inline (mempty
397+ & type_ ?~ OpenApiInteger
398+ & format ?~ "int32" )) ]
399+
400+paramsDefinitionExampleJSON :: Value
401+paramsDefinitionExampleJSON = [aesonQQ|
402+{
403+ "skipParam": {
404+ "name": "skip",
405+ "in": "query",
406+ "description": "number of items to skip",
407+ "required": true,
408+ "schema": {
409+ "type": "integer",
410+ "format": "int32"
411+ }
412+ },
413+ "limitParam": {
414+ "name": "limit",
415+ "in": "query",
416+ "description": "max records to return",
417+ "required": true,
418+ "schema": {
419+ "type": "integer",
420+ "format": "int32"
421+ }
422+ }
423+}
424+|]
425+
426+-- =======================================================================
427+-- Responses Definition object
428+-- =======================================================================
429+
430+responsesDefinitionExample :: HashMap Text Response
431+responsesDefinitionExample =
432+ [ ("NotFound", mempty & description .~ "Entity not found.")
433+ , ("IllegalInput", mempty & description .~ "Illegal input for operation.") ]
434+
435+responsesDefinitionExampleJSON :: Value
436+responsesDefinitionExampleJSON = [aesonQQ|
437+{
438+ "NotFound": {
439+ "description": "Entity not found."
440+ },
441+ "IllegalInput": {
442+ "description": "Illegal input for operation."
443+ }
444+}
445+|]
446+
447+-- =======================================================================
448+-- Security Definition object
449+-- =======================================================================
450+
451+securityDefinitionsExample :: SecurityDefinitions
452+securityDefinitionsExample = SecurityDefinitions
453+ [ ("api_key", SecurityScheme
454+ { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
455+ , _securitySchemeDescription = Nothing })
456+ , ("petstore_auth", SecurityScheme
457+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
458+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
459+ , _oAath2RefreshUrl = Nothing
460+ , _oAuth2Scopes =
461+ [ ("write:pets", "modify pets in your account")
462+ , ("read:pets", "read your pets") ] } )
463+ , _securitySchemeDescription = Nothing }) ]
464+
465+securityDefinitionsExampleJSON :: Value
466+securityDefinitionsExampleJSON = [aesonQQ|
467+{
468+ "api_key": {
469+ "in": "header",
470+ "name": "api_key",
471+ "type": "apiKey"
472+ },
473+ "petstore_auth": {
474+ "type": "oauth2",
475+ "flows": {
476+ "implicit": {
477+ "scopes": {
478+ "write:pets": "modify pets in your account",
479+ "read:pets": "read your pets"
480+ },
481+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
482+ }
483+ }
484+ }
485+}
486+
487+|]
488+
489+oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
490+oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
491+ [ ("petstore_auth", SecurityScheme
492+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
493+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
494+ , _oAath2RefreshUrl = Nothing
495+ , _oAuth2Scopes =
496+ [ ("read:pets", "read your pets") ] } )
497+ , _securitySchemeDescription = Nothing })
498+ ]
499+
500+oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
501+oAuth2SecurityDefinitionsWriteExample = SecurityDefinitions
502+ [ ("petstore_auth", SecurityScheme
503+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
504+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
505+ , _oAath2RefreshUrl = Nothing
506+ , _oAuth2Scopes =
507+ [ ("write:pets", "modify pets in your account") ] } )
508+ , _securitySchemeDescription = Nothing })
509+ ]
510+
511+oAuth2SecurityDefinitionsExample :: SecurityDefinitions
512+oAuth2SecurityDefinitionsExample =
513+ oAuth2SecurityDefinitionsWriteExample <>
514+ oAuth2SecurityDefinitionsReadExample
515+
516+oAuth2SecurityDefinitionsExampleJSON :: Value
517+oAuth2SecurityDefinitionsExampleJSON = [aesonQQ|
518+{
519+ "petstore_auth": {
520+ "type": "oauth2",
521+ "flows": {
522+ "implicit": {
523+ "scopes": {
524+ "write:pets": "modify pets in your account",
525+ "read:pets": "read your pets"
526+ },
527+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
528+ }
529+ }
530+ }
531+}
532+|]
533+
534+oAuth2SecurityDefinitionsReadOpenApi :: OpenApi
535+oAuth2SecurityDefinitionsReadOpenApi =
536+ mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsReadExample
537+
538+oAuth2SecurityDefinitionsWriteOpenApi :: OpenApi
539+oAuth2SecurityDefinitionsWriteOpenApi =
540+ mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsWriteExample
541+
542+oAuth2SecurityDefinitionsOpenApi :: OpenApi
543+oAuth2SecurityDefinitionsOpenApi =
544+ mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsExample
545+
546+-- =======================================================================
547+-- Swagger object
548+-- =======================================================================
549+
550+emptyPathsFieldExample :: OpenApi
551+emptyPathsFieldExample = mempty
552+
553+emptyPathsFieldExampleJSON :: Value
554+emptyPathsFieldExampleJSON = [aesonQQ|
555+{
556+ "openapi": "3.0.0",
557+ "info": {"version": "", "title": ""},
558+ "paths": {},
559+ "components": {}
560+}
561+|]
562+
563+swaggerExample :: OpenApi
564+swaggerExample = mempty
565+ -- & basePath ?~ "/"
566+ -- & schemes ?~ [Http]
567+ & info .~ (mempty
568+ & version .~ "1.0"
569+ & title .~ "Todo API"
570+ & license ?~ "MIT"
571+ & license._Just.url ?~ URL "http://mit.com"
572+ & description ?~ "This is an API that tests servant-swagger support for a Todo API")
573+ & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
574+ & responses . at 200 ?~ Inline (mempty
575+ & description .~ "OK"
576+ & content . at "application/json" ?~ (mempty
577+ & schema ?~ Inline (mempty
578+ & type_ ?~ OpenApiObject
579+ & example ?~ [aesonQQ|
580+ {
581+ "created": 100,
582+ "description": "get milk"
583+ } |]
584+ & description ?~ "This is some real Todo right here"
585+ & properties .~
586+ [ ("created", Inline $ mempty
587+ & type_ ?~ OpenApiInteger
588+ & format ?~ "int32")
589+ , ("description", Inline (mempty & type_ ?~ OpenApiString))])))
590+ & parameters .~
591+ [ Inline $ mempty
592+ & required ?~ True
593+ & name .~ "id"
594+ & description ?~ "TodoId param"
595+ & in_ .~ ParamPath
596+ & schema ?~ Inline (mempty
597+ & type_ ?~ OpenApiString ) ]
598+ & tags .~ InsOrdHS.fromList [ "todo" ] ))
599+
600+swaggerExampleJSON :: Value
601+swaggerExampleJSON = [aesonQQ|
602+{
603+ "openapi": "3.0.0",
604+ "info": {
605+ "version": "1.0",
606+ "title": "Todo API",
607+ "license": {
608+ "url": "http://mit.com",
609+ "name": "MIT"
610+ },
611+ "description": "This is an API that tests servant-swagger support for a Todo API"
612+ },
613+ "paths": {
614+ "/todo/{id}": {
615+ "get": {
616+ "tags": [
617+ "todo"
618+ ],
619+ "parameters": [
620+ {
621+ "required": true,
622+ "schema": {
623+ "type": "string"
624+ },
625+ "in": "path",
626+ "name": "id",
627+ "description": "TodoId param"
628+ }
629+ ],
630+ "responses": {
631+ "200": {
632+ "content": {
633+ "application/json": {
634+ "schema": {
635+ "example": {
636+ "created": 100,
637+ "description": "get milk"
638+ },
639+ "type": "object",
640+ "description": "This is some real Todo right here",
641+ "properties": {
642+ "created": {
643+ "format": "int32",
644+ "type": "integer"
645+ },
646+ "description": {
647+ "type": "string"
648+ }
649+ }
650+ }
651+ }
652+ },
653+ "description": "OK"
654+ }
655+ }
656+ }
657+ }
658+ },
659+ "components": {}
660+}
661+|]
662+
663+petstoreExampleJSON :: Value
664+petstoreExampleJSON = [aesonQQ|
665+{
666+ "openapi": "3.0.0",
667+ "info": {
668+ "version": "1.0.0",
669+ "title": "Swagger Petstore",
670+ "license": {
671+ "name": "MIT"
672+ }
673+ },
674+ "servers": [
675+ {
676+ "url": "http://petstore.swagger.io/v1"
677+ }
678+ ],
679+ "paths": {
680+ "/pets": {
681+ "get": {
682+ "summary": "List all pets",
683+ "operationId": "listPets",
684+ "tags": [
685+ "pets"
686+ ],
687+ "parameters": [
688+ {
689+ "name": "limit",
690+ "in": "query",
691+ "description": "How many items to return at one time (max 100)",
692+ "required": false,
693+ "schema": {
694+ "type": "integer",
695+ "format": "int32"
696+ }
697+ }
698+ ],
699+ "responses": {
700+ "200": {
701+ "description": "A paged array of pets",
702+ "headers": {
703+ "x-next": {
704+ "description": "A link to the next page of responses",
705+ "schema": {
706+ "type": "string"
707+ }
708+ }
709+ },
710+ "content": {
711+ "application/json": {
712+ "schema": {
713+ "type": "array",
714+ "items": {
715+ "type": "object",
716+ "required": [
717+ "id",
718+ "name"
719+ ],
720+ "properties": {
721+ "id": {
722+ "type": "integer",
723+ "format": "int64"
724+ },
725+ "name": {
726+ "type": "string"
727+ },
728+ "tag": {
729+ "type": "string"
730+ }
731+ }
732+ }
733+ }
734+ }
735+ }
736+ },
737+ "default": {
738+ "description": "unexpected error",
739+ "content": {
740+ "application/json": {
741+ "schema": {
742+ "type": "object",
743+ "required": [
744+ "code",
745+ "message"
746+ ],
747+ "properties": {
748+ "code": {
749+ "type": "integer",
750+ "format": "int32"
751+ },
752+ "message": {
753+ "type": "string"
754+ }
755+ }
756+ }
757+ }
758+ }
759+ }
760+ }
761+ },
762+ "post": {
763+ "summary": "Create a pet",
764+ "operationId": "createPets",
765+ "tags": [
766+ "pets"
767+ ],
768+ "responses": {
769+ "201": {
770+ "description": "Null response"
771+ },
772+ "default": {
773+ "description": "unexpected error",
774+ "content": {
775+ "application/json": {
776+ "schema": {
777+ "type": "object",
778+ "required": [
779+ "code",
780+ "message"
781+ ],
782+ "properties": {
783+ "code": {
784+ "type": "integer",
785+ "format": "int32"
786+ },
787+ "message": {
788+ "type": "string"
789+ }
790+ }
791+ }
792+ }
793+ }
794+ }
795+ }
796+ }
797+ },
798+ "/pets/{petId}": {
799+ "get": {
800+ "summary": "Info for a specific pet",
801+ "operationId": "showPetById",
802+ "tags": [
803+ "pets"
804+ ],
805+ "parameters": [
806+ {
807+ "name": "petId",
808+ "in": "path",
809+ "required": true,
810+ "description": "The id of the pet to retrieve",
811+ "schema": {
812+ "type": "string"
813+ }
814+ }
815+ ],
816+ "responses": {
817+ "200": {
818+ "description": "Expected response to a valid request",
819+ "content": {
820+ "application/json": {
821+ "schema": {
822+ "type": "object",
823+ "required": [
824+ "id",
825+ "name"
826+ ],
827+ "properties": {
828+ "id": {
829+ "type": "integer",
830+ "format": "int64"
831+ },
832+ "name": {
833+ "type": "string"
834+ },
835+ "tag": {
836+ "type": "string"
837+ }
838+ }
839+ }
840+ }
841+ }
842+ },
843+ "default": {
844+ "description": "unexpected error",
845+ "content": {
846+ "application/json": {
847+ "schema": {
848+ "type": "object",
849+ "required": [
850+ "code",
851+ "message"
852+ ],
853+ "properties": {
854+ "code": {
855+ "type": "integer",
856+ "format": "int32"
857+ },
858+ "message": {
859+ "type": "string"
860+ }
861+ }
862+ }
863+ }
864+ }
865+ }
866+ }
867+ }
868+ }
869+ },
870+ "components": {
871+ "schemas": {
872+ "Pet": {
873+ "type": "object",
874+ "required": [
875+ "id",
876+ "name"
877+ ],
878+ "properties": {
879+ "id": {
880+ "type": "integer",
881+ "format": "int64"
882+ },
883+ "name": {
884+ "type": "string"
885+ },
886+ "tag": {
887+ "type": "string"
888+ }
889+ }
890+ },
891+ "Pets": {
892+ "type": "array",
893+ "items": {
894+ "type": "object",
895+ "required": [
896+ "id",
897+ "name"
898+ ],
899+ "properties": {
900+ "id": {
901+ "type": "integer",
902+ "format": "int64"
903+ },
904+ "name": {
905+ "type": "string"
906+ },
907+ "tag": {
908+ "type": "string"
909+ }
910+ }
911+ }
912+ },
913+ "Error": {
914+ "type": "object",
915+ "required": [
916+ "code",
917+ "message"
918+ ],
919+ "properties": {
920+ "code": {
921+ "type": "integer",
922+ "format": "int32"
923+ },
924+ "message": {
925+ "type": "string"
926+ }
927+ }
928+ }
929+ }
930+ }
931+}
932+|]
933+
934+compositionSchemaExample :: Schema
935+compositionSchemaExample = mempty
936+ & type_ ?~ OpenApiObject
937+ & Data.OpenApi.allOf ?~ [
938+ Ref (Reference "Other")
939+ , Inline (mempty
940+ & type_ ?~ OpenApiObject
941+ & properties .~
942+ [ ("greet", Inline $ mempty
943+ & type_ ?~ OpenApiString) ])
944+ ]
945+
946+compositionSchemaExampleJSON :: Value
947+compositionSchemaExampleJSON = [aesonQQ|
948+{
949+ "type": "object",
950+ "allOf": [
951+ {
952+ "$ref": "#/components/schemas/Other"
953+ },
954+ {
955+ "type": "object",
956+ "properties": {
957+ "greet": { "type": "string" }
958+ }
959+ }
960+ ]
961+}
962+|]
test/Data/SwaggerSpec.hsdeleted−1661
@@ -1,1661 +0,0 @@
1−{-# LANGUAGE OverloadedLists #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE QuasiQuotes #-}
5−module Data.SwaggerSpec where
6−
7−import Prelude ()
8−import Prelude.Compat
9−
10−import Control.Lens
11−
12−import Data.Aeson
13−import Data.Aeson.QQ.Simple
14−import Data.HashMap.Strict (HashMap)
15−import qualified Data.Set as Set
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 "Composition Schema Example" $ compositionSchemaExample <=> compositionSchemaExampleJSON
47− describe "Swagger Object" $ do
48− context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
49− context "PetStore Example" $
50− it "decodes successfully" $ do
51− fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: Swagger) -> True; _ -> False)
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 .~ Set.fromList ["pet"]
131− & summary ?~ "Updates a pet in the store with form data"
132− & description ?~ ""
133− & operationId ?~ "updatePetWithForm"
134− & consumes ?~ MimeList ["application/x-www-form-urlencoded"]
135− & produces ?~ MimeList ["application/json", "application/xml"]
136− & parameters .~ map Inline
137− [ mempty
138− & name .~ "petId"
139− & description ?~ "ID of pet that needs to be updated"
140− & required ?~ True
141− & schema .~ ParamOther (stringSchema ParamPath)
142− , mempty
143− & name .~ "name"
144− & description ?~ "Updated name of the pet"
145− & required ?~ False
146− & schema .~ ParamOther (stringSchema ParamFormData)
147− , mempty
148− & name .~ "status"
149− & description ?~ "Updated status of the pet"
150− & required ?~ False
151− & schema .~ ParamOther (stringSchema ParamFormData)
152− ]
153−
154− & at 200 ?~ "Pet updated."
155− & at 405 ?~ "Invalid input"
156− & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
157− where
158− stringSchema :: ParamLocation -> ParamOtherSchema
159− stringSchema loc = mempty
160− & in_ .~ loc
161− & type_ .~ SwaggerString
162−
163−operationExampleJSON :: Value
164−operationExampleJSON = [aesonQQ|
165−{
166− "tags": [
167− "pet"
168− ],
169− "summary": "Updates a pet in the store with form data",
170− "description": "",
171− "operationId": "updatePetWithForm",
172− "consumes": [
173− "application/x-www-form-urlencoded"
174− ],
175− "produces": [
176− "application/json",
177− "application/xml"
178− ],
179− "parameters": [
180− {
181− "name": "petId",
182− "in": "path",
183− "description": "ID of pet that needs to be updated",
184− "required": true,
185− "type": "string"
186− },
187− {
188− "name": "name",
189− "in": "formData",
190− "description": "Updated name of the pet",
191− "required": false,
192− "type": "string"
193− },
194− {
195− "name": "status",
196− "in": "formData",
197− "description": "Updated status of the pet",
198− "required": false,
199− "type": "string"
200− }
201− ],
202− "responses": {
203− "200": {
204− "description": "Pet updated."
205− },
206− "405": {
207− "description": "Invalid input"
208− }
209− },
210− "security": [
211− {
212− "petstore_auth": [
213− "write:pets",
214− "read:pets"
215− ]
216− }
217− ]
218−}
219−|]
220−
221−-- =======================================================================
222−-- Schema object
223−-- =======================================================================
224−
225−schemaPrimitiveExample :: Schema
226−schemaPrimitiveExample = mempty
227− & type_ .~ SwaggerString
228− & format ?~ "email"
229−
230−schemaPrimitiveExampleJSON :: Value
231−schemaPrimitiveExampleJSON = [aesonQQ|
232−{
233− "type": "string",
234− "format": "email"
235−}
236−|]
237−
238−schemaSimpleModelExample :: Schema
239−schemaSimpleModelExample = mempty
240− & type_ .~ SwaggerObject
241− & required .~ [ "name" ]
242− & properties .~
243− [ ("name", Inline (mempty & type_ .~ SwaggerString))
244− , ("address", Ref (Reference "Address"))
245− , ("age", Inline $ mempty
246− & minimum_ ?~ 0
247− & type_ .~ SwaggerInteger
248− & format ?~ "int32" ) ]
249−
250−schemaSimpleModelExampleJSON :: Value
251−schemaSimpleModelExampleJSON = [aesonQQ|
252−{
253− "type": "object",
254− "required": [
255− "name"
256− ],
257− "properties": {
258− "name": {
259− "type": "string"
260− },
261− "address": {
262− "$ref": "#/definitions/Address"
263− },
264− "age": {
265− "type": "integer",
266− "format": "int32",
267− "minimum": 0
268− }
269− }
270−}
271−|]
272−
273−schemaModelDictExample :: Schema
274−schemaModelDictExample = mempty
275− & type_ .~ SwaggerObject
276− & additionalProperties ?~ AdditionalPropertiesSchema (Inline (mempty & type_ .~ SwaggerString))
277−
278−schemaModelDictExampleJSON :: Value
279−schemaModelDictExampleJSON = [aesonQQ|
280−{
281− "type": "object",
282− "additionalProperties": {
283− "type": "string"
284− }
285−}
286−|]
287−
288−schemaAdditionalExample :: Schema
289−schemaAdditionalExample = mempty
290− & type_ .~ SwaggerObject
291− & additionalProperties ?~ AdditionalPropertiesAllowed True
292−
293−schemaAdditionalExampleJSON :: Value
294−schemaAdditionalExampleJSON = [aesonQQ|
295−{
296− "type": "object",
297− "additionalProperties": true
298−}
299−|]
300−
301−schemaWithExampleExample :: Schema
302−schemaWithExampleExample = mempty
303− & type_ .~ SwaggerObject
304− & properties .~
305− [ ("id", Inline $ mempty
306− & type_ .~ SwaggerInteger
307− & format ?~ "int64" )
308− , ("name", Inline $ mempty
309− & type_ .~ SwaggerString) ]
310− & required .~ [ "name" ]
311− & example ?~ [aesonQQ|
312− {
313− "name": "Puma",
314− "id": 1
315− }
316− |]
317−
318−schemaWithExampleExampleJSON :: Value
319−schemaWithExampleExampleJSON = [aesonQQ|
320−{
321− "type": "object",
322− "properties": {
323− "id": {
324− "type": "integer",
325− "format": "int64"
326− },
327− "name": {
328− "type": "string"
329− }
330− },
331− "required": [
332− "name"
333− ],
334− "example": {
335− "name": "Puma",
336− "id": 1
337− }
338−}
339−|]
340−
341−-- =======================================================================
342−-- Definitions object
343−-- =======================================================================
344−
345−definitionsExample :: HashMap Text Schema
346−definitionsExample =
347− [ ("Category", mempty
348− & type_ .~ SwaggerObject
349− & properties .~
350− [ ("id", Inline $ mempty
351− & type_ .~ SwaggerInteger
352− & format ?~ "int64")
353− , ("name", Inline (mempty & type_ .~ SwaggerString)) ] )
354− , ("Tag", mempty
355− & type_ .~ SwaggerObject
356− & properties .~
357− [ ("id", Inline $ mempty
358− & type_ .~ SwaggerInteger
359− & format ?~ "int64")
360− , ("name", Inline (mempty & type_ .~ SwaggerString)) ] ) ]
361−
362−definitionsExampleJSON :: Value
363−definitionsExampleJSON = [aesonQQ|
364−{
365− "Category": {
366− "type": "object",
367− "properties": {
368− "id": {
369− "type": "integer",
370− "format": "int64"
371− },
372− "name": {
373− "type": "string"
374− }
375− }
376− },
377− "Tag": {
378− "type": "object",
379− "properties": {
380− "id": {
381− "type": "integer",
382− "format": "int64"
383− },
384− "name": {
385− "type": "string"
386− }
387− }
388− }
389−}
390−|]
391−
392−-- =======================================================================
393−-- Parameters Definition object
394−-- =======================================================================
395−
396−paramsDefinitionExample :: HashMap Text Param
397−paramsDefinitionExample =
398− [ ("skipParam", mempty
399− & name .~ "skip"
400− & description ?~ "number of items to skip"
401− & required ?~ True
402− & schema .~ ParamOther (mempty
403− & in_ .~ ParamQuery
404− & type_ .~ SwaggerInteger
405− & format ?~ "int32" ))
406− , ("limitParam", mempty
407− & name .~ "limit"
408− & description ?~ "max records to return"
409− & required ?~ True
410− & schema .~ ParamOther (mempty
411− & in_ .~ ParamQuery
412− & type_ .~ SwaggerInteger
413− & format ?~ "int32" )) ]
414−
415−paramsDefinitionExampleJSON :: Value
416−paramsDefinitionExampleJSON = [aesonQQ|
417−{
418− "skipParam": {
419− "name": "skip",
420− "in": "query",
421− "description": "number of items to skip",
422− "required": true,
423− "type": "integer",
424− "format": "int32"
425− },
426− "limitParam": {
427− "name": "limit",
428− "in": "query",
429− "description": "max records to return",
430− "required": true,
431− "type": "integer",
432− "format": "int32"
433− }
434−}
435−|]
436−
437−-- =======================================================================
438−-- Responses Definition object
439−-- =======================================================================
440−
441−responsesDefinitionExample :: HashMap Text Response
442−responsesDefinitionExample =
443− [ ("NotFound", mempty & description .~ "Entity not found.")
444− , ("IllegalInput", mempty & description .~ "Illegal input for operation.") ]
445−
446−responsesDefinitionExampleJSON :: Value
447−responsesDefinitionExampleJSON = [aesonQQ|
448−{
449− "NotFound": {
450− "description": "Entity not found."
451− },
452− "IllegalInput": {
453− "description": "Illegal input for operation."
454− }
455−}
456−|]
457−
458−-- =======================================================================
459−-- Responses Definition object
460−-- =======================================================================
461−
462−securityDefinitionsExample :: HashMap Text SecurityScheme
463−securityDefinitionsExample =
464− [ ("api_key", SecurityScheme
465− { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
466− , _securitySchemeDescription = Nothing })
467− , ("petstore_auth", SecurityScheme
468− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
469− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
470− , _oauth2Scopes =
471− [ ("write:pets", "modify pets in your account")
472− , ("read:pets", "read your pets") ] } )
473− , _securitySchemeDescription = Nothing }) ]
474−
475−securityDefinitionsExampleJSON :: Value
476−securityDefinitionsExampleJSON = [aesonQQ|
477−{
478− "api_key": {
479− "type": "apiKey",
480− "name": "api_key",
481− "in": "header"
482− },
483− "petstore_auth": {
484− "type": "oauth2",
485− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
486− "flow": "implicit",
487− "scopes": {
488− "write:pets": "modify pets in your account",
489− "read:pets": "read your pets"
490− }
491− }
492−}
493−|]
494−
495−-- =======================================================================
496−-- Swagger object
497−-- =======================================================================
498−
499−swaggerExample :: Swagger
500−swaggerExample = mempty
501− & basePath ?~ "/"
502− & schemes ?~ [Http]
503− & info .~ (mempty
504− & version .~ "1.0"
505− & title .~ "Todo API"
506− & license ?~ "MIT"
507− & license._Just.url ?~ URL "http://mit.com"
508− & description ?~ "This is a an API that tests servant-swagger support for a Todo API")
509− & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
510− & at 200 ?~ Inline (mempty
511− & description .~ "OK"
512− & schema ?~ Inline (mempty
513− & type_ .~ SwaggerObject
514− & example ?~ [aesonQQ|
515− {
516− "created": 100,
517− "description": "get milk"
518− } |]
519− & description ?~ "This is some real Todo right here"
520− & properties .~
521− [ ("created", Inline $ mempty
522− & type_ .~ SwaggerInteger
523− & format ?~ "int32")
524− , ("description", Inline (mempty & type_ .~ SwaggerString))]))
525− & produces ?~ MimeList [ "application/json" ]
526− & parameters .~
527− [ Inline $ mempty
528− & required ?~ True
529− & name .~ "id"
530− & description ?~ "TodoId param"
531− & schema .~ ParamOther (mempty
532− & in_ .~ ParamPath
533− & type_ .~ SwaggerString ) ]
534− & tags .~ Set.fromList [ "todo" ] ))
535−
536−swaggerExampleJSON :: Value
537−swaggerExampleJSON = [aesonQQ|
538−{
539− "swagger": "2.0",
540− "basePath": "/",
541− "schemes": [
542− "http"
543− ],
544− "info": {
545− "version": "1.0",
546− "title": "Todo API",
547− "license": {
548− "url": "http://mit.com",
549− "name": "MIT"
550− },
551− "description": "This is a an API that tests servant-swagger support for a Todo API"
552− },
553− "paths": {
554− "/todo/{id}": {
555− "get": {
556− "responses": {
557− "200": {
558− "schema": {
559− "example": {
560− "created": 100,
561− "description": "get milk"
562− },
563− "type": "object",
564− "description": "This is some real Todo right here",
565− "properties": {
566− "created": {
567− "format": "int32",
568− "type": "integer"
569− },
570− "description": {
571− "type": "string"
572− }
573− }
574− },
575− "description": "OK"
576− }
577− },
578− "produces": [
579− "application/json"
580− ],
581− "parameters": [
582− {
583− "required": true,
584− "in": "path",
585− "name": "id",
586− "type": "string",
587− "description": "TodoId param"
588− }
589− ],
590− "tags": [
591− "todo"
592− ]
593− }
594− }
595− }
596−}
597−|]
598−
599−petstoreExampleJSON :: Value
600−petstoreExampleJSON = [aesonQQ|
601−{
602− "swagger":"2.0",
603− "info":{
604− "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.",
605− "version":"1.0.0",
606− "title":"Swagger Petstore",
607− "termsOfService":"http://swagger.io/terms/",
608− "contact":{
609− "email":"apiteam@swagger.io"
610− },
611− "license":{
612− "name":"Apache 2.0",
613− "url":"http://www.apache.org/licenses/LICENSE-2.0.html"
614− }
615− },
616− "host":"petstore.swagger.io",
617− "basePath":"/v2",
618− "tags":[
619− {
620− "name":"pet",
621− "description":"Everything about your Pets",
622− "externalDocs":{
623− "description":"Find out more",
624− "url":"http://swagger.io"
625− }
626− },
627− {
628− "name":"store",
629− "description":"Access to Petstore orders"
630− },
631− {
632− "name":"user",
633− "description":"Operations about user",
634− "externalDocs":{
635− "description":"Find out more about our store",
636− "url":"http://swagger.io"
637− }
638− }
639− ],
640− "schemes":[
641− "http"
642− ],
643− "paths":{
644− "/pet":{
645− "post":{
646− "tags":[
647− "pet"
648− ],
649− "summary":"Add a new pet to the store",
650− "description":"",
651− "operationId":"addPet",
652− "consumes":[
653− "application/json",
654− "application/xml"
655− ],
656− "produces":[
657− "application/xml",
658− "application/json"
659− ],
660− "parameters":[
661− {
662− "in":"body",
663− "name":"body",
664− "description":"Pet object that needs to be added to the store",
665− "required":true,
666− "schema":{
667− "$ref":"#/definitions/Pet"
668− }
669− }
670− ],
671− "responses":{
672− "405":{
673− "description":"Invalid input"
674− }
675− },
676− "security":[
677− {
678− "petstore_auth":[
679− "write:pets",
680− "read:pets"
681− ]
682− }
683− ]
684− },
685− "put":{
686− "tags":[
687− "pet"
688− ],
689− "summary":"Update an existing pet",
690− "description":"",
691− "operationId":"updatePet",
692− "consumes":[
693− "application/json",
694− "application/xml"
695− ],
696− "produces":[
697− "application/xml",
698− "application/json"
699− ],
700− "parameters":[
701− {
702− "in":"body",
703− "name":"body",
704− "description":"Pet object that needs to be added to the store",
705− "required":true,
706− "schema":{
707− "$ref":"#/definitions/Pet"
708− }
709− }
710− ],
711− "responses":{
712− "400":{
713− "description":"Invalid ID supplied"
714− },
715− "404":{
716− "description":"Pet not found"
717− },
718− "405":{
719− "description":"Validation exception"
720− }
721− },
722− "security":[
723− {
724− "petstore_auth":[
725− "write:pets",
726− "read:pets"
727− ]
728− }
729− ]
730− }
731− },
732− "/pet/findByStatus":{
733− "get":{
734− "tags":[
735− "pet"
736− ],
737− "summary":"Finds Pets by status",
738− "description":"Multiple status values can be provided with comma seperated strings",
739− "operationId":"findPetsByStatus",
740− "produces":[
741− "application/xml",
742− "application/json"
743− ],
744− "parameters":[
745− {
746− "name":"status",
747− "in":"query",
748− "description":"Status values that need to be considered for filter",
749− "required":true,
750− "type":"array",
751− "items":{
752− "type":"string",
753− "enum":[
754− "available",
755− "pending",
756− "sold"
757− ],
758− "default":"available"
759− },
760− "collectionFormat":"csv"
761− }
762− ],
763− "responses":{
764− "200":{
765− "description":"successful operation",
766− "schema":{
767− "type":"array",
768− "items":{
769− "$ref":"#/definitions/Pet"
770− }
771− }
772− },
773− "400":{
774− "description":"Invalid status value"
775− }
776− },
777− "security":[
778− {
779− "petstore_auth":[
780− "write:pets",
781− "read:pets"
782− ]
783− }
784− ]
785− }
786− },
787− "/pet/findByTags":{
788− "get":{
789− "tags":[
790− "pet"
791− ],
792− "summary":"Finds Pets by tags",
793− "description":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.",
794− "operationId":"findPetsByTags",
795− "produces":[
796− "application/xml",
797− "application/json"
798− ],
799− "parameters":[
800− {
801− "name":"tags",
802− "in":"query",
803− "description":"Tags to filter by",
804− "required":true,
805− "type":"array",
806− "items":{
807− "type":"string"
808− },
809− "collectionFormat":"csv"
810− }
811− ],
812− "responses":{
813− "200":{
814− "description":"successful operation",
815− "schema":{
816− "type":"array",
817− "items":{
818− "$ref":"#/definitions/Pet"
819− }
820− }
821− },
822− "400":{
823− "description":"Invalid tag value"
824− }
825− },
826− "security":[
827− {
828− "petstore_auth":[
829− "write:pets",
830− "read:pets"
831− ]
832− }
833− ]
834− }
835− },
836− "/pet/{petId}":{
837− "get":{
838− "tags":[
839− "pet"
840− ],
841− "summary":"Find pet by ID",
842− "description":"Returns a single pet",
843− "operationId":"getPetById",
844− "produces":[
845− "application/xml",
846− "application/json"
847− ],
848− "parameters":[
849− {
850− "name":"petId",
851− "in":"path",
852− "description":"ID of pet to return",
853− "required":true,
854− "type":"integer",
855− "format":"int64"
856− }
857− ],
858− "responses":{
859− "200":{
860− "description":"successful operation",
861− "schema":{
862− "$ref":"#/definitions/Pet"
863− }
864− },
865− "400":{
866− "description":"Invalid ID supplied"
867− },
868− "404":{
869− "description":"Pet not found"
870− }
871− },
872− "security":[
873− {
874− "api_key": []
875− }
876− ]
877− },
878− "post":{
879− "tags":[
880− "pet"
881− ],
882− "summary":"Updates a pet in the store with form data",
883− "description":"",
884− "operationId":"updatePetWithForm",
885− "consumes":[
886− "application/x-www-form-urlencoded"
887− ],
888− "produces":[
889− "application/xml",
890− "application/json"
891− ],
892− "parameters":[
893− {
894− "name":"petId",
895− "in":"path",
896− "description":"ID of pet that needs to be updated",
897− "required":true,
898− "type":"integer",
899− "format":"int64"
900− },
901− {
902− "name":"name",
903− "in":"formData",
904− "description":"Updated name of the pet",
905− "required":false,
906− "type":"string"
907− },
908− {
909− "name":"status",
910− "in":"formData",
911− "description":"Updated status of the pet",
912− "required":false,
913− "type":"string"
914− }
915− ],
916− "responses":{
917− "405":{
918− "description":"Invalid input"
919− }
920− },
921− "security":[
922− {
923− "petstore_auth":[
924− "write:pets",
925− "read:pets"
926− ]
927− }
928− ]
929− },
930− "delete":{
931− "tags":[
932− "pet"
933− ],
934− "summary":"Deletes a pet",
935− "description":"",
936− "operationId":"deletePet",
937− "produces":[
938− "application/xml",
939− "application/json"
940− ],
941− "parameters":[
942− {
943− "name":"api_key",
944− "in":"header",
945− "required":false,
946− "type":"string"
947− },
948− {
949− "name":"petId",
950− "in":"path",
951− "description":"Pet id to delete",
952− "required":true,
953− "type":"integer",
954− "format":"int64"
955− }
956− ],
957− "responses":{
958− "400":{
959− "description":"Invalid pet value"
960− }
961− },
962− "security":[
963− {
964− "petstore_auth":[
965− "write:pets",
966− "read:pets"
967− ]
968− }
969− ]
970− }
971− },
972− "/pet/{petId}/uploadImage":{
973− "post":{
974− "tags":[
975− "pet"
976− ],
977− "summary":"uploads an image",
978− "description":"",
979− "operationId":"uploadFile",
980− "consumes":[
981− "multipart/form-data"
982− ],
983− "produces":[
984− "application/json"
985− ],
986− "parameters":[
987− {
988− "name":"petId",
989− "in":"path",
990− "description":"ID of pet to update",
991− "required":true,
992− "type":"integer",
993− "format":"int64"
994− },
995− {
996− "name":"additionalMetadata",
997− "in":"formData",
998− "description":"Additional data to pass to server",
999− "required":false,
1000− "type":"string"
1001− },
1002− {
1003− "name":"file",
1004− "in":"formData",
1005− "description":"file to upload",
1006− "required":false,
1007− "type":"file"
1008− }
1009− ],
1010− "responses":{
1011− "200":{
1012− "description":"successful operation",
1013− "schema":{
1014− "$ref":"#/definitions/ApiResponse"
1015− }
1016− }
1017− },
1018− "security":[
1019− {
1020− "petstore_auth":[
1021− "write:pets",
1022− "read:pets"
1023− ]
1024− }
1025− ]
1026− }
1027− },
1028− "/store/inventory":{
1029− "get":{
1030− "tags":[
1031− "store"
1032− ],
1033− "summary":"Returns pet inventories by status",
1034− "description":"Returns a map of status codes to quantities",
1035− "operationId":"getInventory",
1036− "produces":[
1037− "application/json"
1038− ],
1039− "parameters": [],
1040− "responses":{
1041− "200":{
1042− "description":"successful operation",
1043− "schema":{
1044− "type":"object",
1045− "additionalProperties":{
1046− "type":"integer",
1047− "format":"int32"
1048− }
1049− }
1050− }
1051− },
1052− "security":[
1053− {
1054− "api_key": []
1055− }
1056− ]
1057− }
1058− },
1059− "/store/order":{
1060− "post":{
1061− "tags":[
1062− "store"
1063− ],
1064− "summary":"Place an order for a pet",
1065− "description":"",
1066− "operationId":"placeOrder",
1067− "produces":[
1068− "application/xml",
1069− "application/json"
1070− ],
1071− "parameters":[
1072− {
1073− "in":"body",
1074− "name":"body",
1075− "description":"order placed for purchasing the pet",
1076− "required":true,
1077− "schema":{
1078− "$ref":"#/definitions/Order"
1079− }
1080− }
1081− ],
1082− "responses":{
1083− "200":{
1084− "description":"successful operation",
1085− "schema":{
1086− "$ref":"#/definitions/Order"
1087− }
1088− },
1089− "400":{
1090− "description":"Invalid Order"
1091− }
1092− }
1093− }
1094− },
1095− "/store/order/{orderId}":{
1096− "get":{
1097− "tags":[
1098− "store"
1099− ],
1100− "summary":"Find purchase order by ID",
1101− "description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",
1102− "operationId":"getOrderById",
1103− "produces":[
1104− "application/xml",
1105− "application/json"
1106− ],
1107− "parameters":[
1108− {
1109− "name":"orderId",
1110− "in":"path",
1111− "description":"ID of pet that needs to be fetched",
1112− "required":true,
1113− "type":"integer",
1114− "maximum":5.0,
1115− "minimum":1.0,
1116− "format":"int64"
1117− }
1118− ],
1119− "responses":{
1120− "200":{
1121− "description":"successful operation",
1122− "schema":{
1123− "$ref":"#/definitions/Order"
1124− }
1125− },
1126− "400":{
1127− "description":"Invalid ID supplied"
1128− },
1129− "404":{
1130− "description":"Order not found"
1131− }
1132− }
1133− },
1134− "delete":{
1135− "tags":[
1136− "store"
1137− ],
1138− "summary":"Delete purchase order by ID",
1139− "description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
1140− "operationId":"deleteOrder",
1141− "produces":[
1142− "application/xml",
1143− "application/json"
1144− ],
1145− "parameters":[
1146− {
1147− "name":"orderId",
1148− "in":"path",
1149− "description":"ID of the order that needs to be deleted",
1150− "required":true,
1151− "type":"string",
1152− "minimum":1.0
1153− }
1154− ],
1155− "responses":{
1156− "400":{
1157− "description":"Invalid ID supplied"
1158− },
1159− "404":{
1160− "description":"Order not found"
1161− }
1162− }
1163− }
1164− },
1165− "/user":{
1166− "post":{
1167− "tags":[
1168− "user"
1169− ],
1170− "summary":"Create user",
1171− "description":"This can only be done by the logged in user.",
1172− "operationId":"createUser",
1173− "produces":[
1174− "application/xml",
1175− "application/json"
1176− ],
1177− "parameters":[
1178− {
1179− "in":"body",
1180− "name":"body",
1181− "description":"Created user object",
1182− "required":true,
1183− "schema":{
1184− "$ref":"#/definitions/User"
1185− }
1186− }
1187− ],
1188− "responses":{
1189− "default":{
1190− "description":"successful operation"
1191− }
1192− }
1193− }
1194− },
1195− "/user/createWithArray":{
1196− "post":{
1197− "tags":[
1198− "user"
1199− ],
1200− "summary":"Creates list of users with given input array",
1201− "description":"",
1202− "operationId":"createUsersWithArrayInput",
1203− "produces":[
1204− "application/xml",
1205− "application/json"
1206− ],
1207− "parameters":[
1208− {
1209− "in":"body",
1210− "name":"body",
1211− "description":"List of user object",
1212− "required":true,
1213− "schema":{
1214− "type":"array",
1215− "items":{
1216− "$ref":"#/definitions/User"
1217− }
1218− }
1219− }
1220− ],
1221− "responses":{
1222− "default":{
1223− "description":"successful operation"
1224− }
1225− }
1226− }
1227− },
1228− "/user/createWithList":{
1229− "post":{
1230− "tags":[
1231− "user"
1232− ],
1233− "summary":"Creates list of users with given input array",
1234− "description":"",
1235− "operationId":"createUsersWithListInput",
1236− "produces":[
1237− "application/xml",
1238− "application/json"
1239− ],
1240− "parameters":[
1241− {
1242− "in":"body",
1243− "name":"body",
1244− "description":"List of user object",
1245− "required":true,
1246− "schema":{
1247− "type":"array",
1248− "items":{
1249− "$ref":"#/definitions/User"
1250− }
1251− }
1252− }
1253− ],
1254− "responses":{
1255− "default":{
1256− "description":"successful operation"
1257− }
1258− }
1259− }
1260− },
1261− "/user/login":{
1262− "get":{
1263− "tags":[
1264− "user"
1265− ],
1266− "summary":"Logs user into the system",
1267− "description":"",
1268− "operationId":"loginUser",
1269− "produces":[
1270− "application/xml",
1271− "application/json"
1272− ],
1273− "parameters":[
1274− {
1275− "name":"username",
1276− "in":"query",
1277− "description":"The user name for login",
1278− "required":true,
1279− "type":"string"
1280− },
1281− {
1282− "name":"password",
1283− "in":"query",
1284− "description":"The password for login in clear text",
1285− "required":true,
1286− "type":"string"
1287− }
1288− ],
1289− "responses":{
1290− "200":{
1291− "description":"successful operation",
1292− "schema":{
1293− "type":"string"
1294− },
1295− "headers":{
1296− "X-Rate-Limit":{
1297− "type":"integer",
1298− "format":"int32",
1299− "description":"calls per hour allowed by the user"
1300− },
1301− "X-Expires-After":{
1302− "type":"string",
1303− "format":"date-time",
1304− "description":"date in UTC when toekn expires"
1305− }
1306− }
1307− },
1308− "400":{
1309− "description":"Invalid username/password supplied"
1310− }
1311− }
1312− }
1313− },
1314− "/user/logout":{
1315− "get":{
1316− "tags":[
1317− "user"
1318− ],
1319− "summary":"Logs out current logged in user session",
1320− "description":"",
1321− "operationId":"logoutUser",
1322− "produces":[
1323− "application/xml",
1324− "application/json"
1325− ],
1326− "parameters": [],
1327− "responses":{
1328− "default":{
1329− "description":"successful operation"
1330− }
1331− }
1332− }
1333− },
1334− "/user/{username}":{
1335− "get":{
1336− "tags":[
1337− "user"
1338− ],
1339− "summary":"Get user by user name",
1340− "description":"",
1341− "operationId":"getUserByName",
1342− "produces":[
1343− "application/xml",
1344− "application/json"
1345− ],
1346− "parameters":[
1347− {
1348− "name":"username",
1349− "in":"path",
1350− "description":"The name that needs to be fetched. Use user1 for testing. ",
1351− "required":true,
1352− "type":"string"
1353− }
1354− ],
1355− "responses":{
1356− "200":{
1357− "description":"successful operation",
1358− "schema":{
1359− "$ref":"#/definitions/User"
1360− }
1361− },
1362− "400":{
1363− "description":"Invalid username supplied"
1364− },
1365− "404":{
1366− "description":"User not found"
1367− }
1368− }
1369− },
1370− "put":{
1371− "tags":[
1372− "user"
1373− ],
1374− "summary":"Updated user",
1375− "description":"This can only be done by the logged in user.",
1376− "operationId":"updateUser",
1377− "produces":[
1378− "application/xml",
1379− "application/json"
1380− ],
1381− "parameters":[
1382− {
1383− "name":"username",
1384− "in":"path",
1385− "description":"name that need to be deleted",
1386− "required":true,
1387− "type":"string"
1388− },
1389− {
1390− "in":"body",
1391− "name":"body",
1392− "description":"Updated user object",
1393− "required":true,
1394− "schema":{
1395− "$ref":"#/definitions/User"
1396− }
1397− }
1398− ],
1399− "responses":{
1400− "400":{
1401− "description":"Invalid user supplied"
1402− },
1403− "404":{
1404− "description":"User not found"
1405− }
1406− }
1407− },
1408− "delete":{
1409− "tags":[
1410− "user"
1411− ],
1412− "summary":"Delete user",
1413− "description":"This can only be done by the logged in user.",
1414− "operationId":"deleteUser",
1415− "produces":[
1416− "application/xml",
1417− "application/json"
1418− ],
1419− "parameters":[
1420− {
1421− "name":"username",
1422− "in":"path",
1423− "description":"The name that needs to be deleted",
1424− "required":true,
1425− "type":"string"
1426− }
1427− ],
1428− "responses":{
1429− "400":{
1430− "description":"Invalid username supplied"
1431− },
1432− "404":{
1433− "description":"User not found"
1434− }
1435− }
1436− }
1437− }
1438− },
1439− "securityDefinitions":{
1440− "petstore_auth":{
1441− "type":"oauth2",
1442− "authorizationUrl":"http://petstore.swagger.io/api/oauth/dialog",
1443− "flow":"implicit",
1444− "scopes":{
1445− "write:pets":"modify pets in your account",
1446− "read:pets":"read your pets"
1447− }
1448− },
1449− "api_key":{
1450− "type":"apiKey",
1451− "name":"api_key",
1452− "in":"header"
1453− }
1454− },
1455− "definitions":{
1456− "Order":{
1457− "type":"object",
1458− "properties":{
1459− "id":{
1460− "type":"integer",
1461− "format":"int64"
1462− },
1463− "petId":{
1464− "type":"integer",
1465− "format":"int64"
1466− },
1467− "quantity":{
1468− "type":"integer",
1469− "format":"int32"
1470− },
1471− "shipDate":{
1472− "type":"string",
1473− "format":"date-time"
1474− },
1475− "status":{
1476− "type":"string",
1477− "description":"Order Status",
1478− "enum":[
1479− "placed",
1480− "approved",
1481− "delivered"
1482− ]
1483− },
1484− "complete":{
1485− "type":"boolean",
1486− "default":false
1487− }
1488− },
1489− "xml":{
1490− "name":"Order"
1491− }
1492− },
1493− "Category":{
1494− "type":"object",
1495− "properties":{
1496− "id":{
1497− "type":"integer",
1498− "format":"int64"
1499− },
1500− "name":{
1501− "type":"string"
1502− }
1503− },
1504− "xml":{
1505− "name":"Category"
1506− }
1507− },
1508− "User":{
1509− "type":"object",
1510− "properties":{
1511− "id":{
1512− "type":"integer",
1513− "format":"int64"
1514− },
1515− "username":{
1516− "type":"string"
1517− },
1518− "firstName":{
1519− "type":"string"
1520− },
1521− "lastName":{
1522− "type":"string"
1523− },
1524− "email":{
1525− "type":"string"
1526− },
1527− "password":{
1528− "type":"string"
1529− },
1530− "phone":{
1531− "type":"string"
1532− },
1533− "userStatus":{
1534− "type":"integer",
1535− "format":"int32",
1536− "description":"User Status"
1537− }
1538− },
1539− "xml":{
1540− "name":"User"
1541− }
1542− },
1543− "Tag":{
1544− "type":"object",
1545− "properties":{
1546− "id":{
1547− "type":"integer",
1548− "format":"int64"
1549− },
1550− "name":{
1551− "type":"string"
1552− }
1553− },
1554− "xml":{
1555− "name":"Tag"
1556− }
1557− },
1558− "Pet":{
1559− "type":"object",
1560− "required":[
1561− "name",
1562− "photoUrls"
1563− ],
1564− "properties":{
1565− "id":{
1566− "type":"integer",
1567− "format":"int64"
1568− },
1569− "category":{
1570− "$ref":"#/definitions/Category"
1571− },
1572− "name":{
1573− "type":"string",
1574− "example":"doggie"
1575− },
1576− "photoUrls":{
1577− "type":"array",
1578− "xml":{
1579− "name":"photoUrl",
1580− "wrapped":true
1581− },
1582− "items":{
1583− "type":"string"
1584− }
1585− },
1586− "tags":{
1587− "type":"array",
1588− "xml":{
1589− "name":"tag",
1590− "wrapped":true
1591− },
1592− "items":{
1593− "$ref":"#/definitions/Tag"
1594− }
1595− },
1596− "status":{
1597− "type":"string",
1598− "description":"pet status in the store",
1599− "enum":[
1600− "available",
1601− "pending",
1602− "sold"
1603− ]
1604− }
1605− },
1606− "xml":{
1607− "name":"Pet"
1608− }
1609− },
1610− "ApiResponse":{
1611− "type":"object",
1612− "properties":{
1613− "code":{
1614− "type":"integer",
1615− "format":"int32"
1616− },
1617− "type":{
1618− "type":"string"
1619− },
1620− "message":{
1621− "type":"string"
1622− }
1623− }
1624− }
1625− },
1626− "externalDocs":{
1627− "description":"Find out more about Swagger",
1628− "url":"http://swagger.io"
1629− }
1630−}
1631−|]
1632−
1633−compositionSchemaExample :: Schema
1634−compositionSchemaExample = mempty
1635− & type_ .~ SwaggerObject
1636− & Data.Swagger.allOf ?~ [
1637− Ref (Reference "Other")
1638− , Inline (mempty
1639− & type_ .~ SwaggerObject
1640− & properties .~
1641− [ ("greet", Inline $ mempty
1642− & type_ .~ SwaggerString) ])
1643− ]
1644−
1645−compositionSchemaExampleJSON :: Value
1646−compositionSchemaExampleJSON = [aesonQQ|
1647−{
1648− "type": "object",
1649− "allOf": [
1650− {
1651− "$ref": "#/definitions/Other"
1652− },
1653− {
1654− "type": "object",
1655− "properties": {
1656− "greet": { "type": "string" }
1657− }
1658− }
1659− ]
1660−}
1661−|]
test/SpecCommon.hs+1−9
@@ -8,15 +8,7 @@ import qualified Data.Vector as Vector
88
99import Test.Hspec
1010
11−isSubJSON :: Value -> Value -> Bool
12−isSubJSON Null _ = True
13−isSubJSON (Object x) (Object y) = HashMap.keys x == HashMap.keys i && F.and i
14− where
15− i = HashMap.intersectionWith isSubJSON x y
16−isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)
17−isSubJSON x y = x == y
18−
19−(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec
11+(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a, HasCallStack) => a -> Value -> Spec
2012x <=> js = do
2113 it "encodes correctly" $ do
2214 toJSON x `shouldBe` js