git.iko.soy/typeable/compaREST
on dcd32cb help sign in
190 files changed+1853−4295
since splitsnapshots
unifiedsplit
.github/actions/common_setup/action.yaml−42 .github/workflows/github-actions-image.yaml−290 .github/workflows/haskell.yml+54 .github/workflows/remove-artifacts.yaml+17 .gitmodules+3 CHANGELOG.md+3−7 LICENSE+1−1 README.md+4−326 action.yaml−48 app/Data/OpenApi/Compare/Options.hs−135 app/FormatHeuristic.hs+3−3 app/Main.hs+8−11 app/OpenAPI/Checker/Options.hs+81 awsm-css+1 benchmarks/README.md+2−2 benchmarks/default.nix+12−8 ci/policeman.nix−4 css/awsm.min.css−1 default.nix−125 docs/Development_guide.md−149 docs/Integration_guide.md−49 docs/Releasing.md−15 docs/User_guide.md−51 docs/img/github-action-report.png docs/img/pre-release-artifacts.png docs/img/pre-release-ci.png docs/img/pre-release.png fourmolu.yaml−8 github-action/CompaREST/GitHub/API.hs−86 github-action/CompaREST/GitHub/Action/Config.hs−47 github-action/Control/Monad/Freer/GitHub.hs−27 github-action/GitHub/Data/Checks.hs−173 github-action/GitHub/Endpoints/Checks.hs−12 github-action/Main.hs−72 nix/ci.nix−11 nix/diff-yaml.nix−20 nix/shell.nix−11 nix/sources.json−50 nix/sources.nix−174 nix/yq.nix−26 compaREST.cabal → openapi-diff.cabal+60−89 scripts/diff-yaml−35 src/Data/HList.hs+11−11 src/Data/OpenApi/Compare/Memo.hs−164 src/Data/OpenApi/Compare/Run.hs−31 src/Data/OpenApi/Compare/Validate/Products.hs−48 src/Data/OpenApi/Compare/Validate/Sums.hs−25 src/Data/OpenUnion/Extra.hs+5−5 src/Data/OpenApi/Compare/Behavior.hs → src/OpenAPI/Checker/Behavior.hs+11−49 src/Data/OpenApi/Compare/Common.hs → src/OpenAPI/Checker/Common.hs+2−2 src/Data/OpenApi/Compare/Formula.hs → src/OpenAPI/Checker/Formula.hs+25−35 src/OpenAPI/Checker/Memo.hs+126 src/Data/OpenApi/Compare/Orphans.hs → src/OpenAPI/Checker/Orphans.hs+1−1 src/Data/OpenApi/Compare/Paths.hs → src/OpenAPI/Checker/Paths.hs+10−10 src/Data/OpenApi/Compare/PathsPrefixTree.hs → src/OpenAPI/Checker/PathsPrefixTree.hs+42−65 src/Data/OpenApi/Compare/References.hs → src/OpenAPI/Checker/References.hs+12−11 src/Data/OpenApi/Compare/Report.hs → src/OpenAPI/Checker/Report.hs+119−176 src/Data/OpenApi/Compare/Report/Html/Template.hs → src/OpenAPI/Checker/Report/Html/Template.hs+5−5 src/Data/OpenApi/Compare/Report/Jet.hs → src/OpenAPI/Checker/Report/Jet.hs+6−6 src/OpenAPI/Checker/Run.hs+30 src/Data/OpenApi/Compare/Subtree.hs → src/OpenAPI/Checker/Subtree.hs+125−132 src/Data/OpenApi/Compare/Validate/Header.hs → src/OpenAPI/Checker/Validate/Header.hs+7−9 src/Data/OpenApi/Compare/Validate/Link.hs → src/OpenAPI/Checker/Validate/Link.hs+6−6 src/Data/OpenApi/Compare/Validate/MediaTypeObject.hs → src/OpenAPI/Checker/Validate/MediaTypeObject.hs+17−17 src/Data/OpenApi/Compare/Validate/OAuth2Flows.hs → src/OpenAPI/Checker/Validate/OAuth2Flows.hs+21−26 src/Data/OpenApi/Compare/Validate/OpenApi.hs → src/OpenAPI/Checker/Validate/OpenApi.hs+18−20 src/Data/OpenApi/Compare/Validate/Operation.hs → src/OpenAPI/Checker/Validate/Operation.hs+50−57 src/Data/OpenApi/Compare/Validate/Param.hs → src/OpenAPI/Checker/Validate/Param.hs+15−20 src/Data/OpenApi/Compare/Validate/PathFragment.hs → src/OpenAPI/Checker/Validate/PathFragment.hs+10−9 src/OpenAPI/Checker/Validate/Products.hs+49 src/Data/OpenApi/Compare/Validate/RequestBody.hs → src/OpenAPI/Checker/Validate/RequestBody.hs+9−10 src/Data/OpenApi/Compare/Validate/Responses.hs → src/OpenAPI/Checker/Validate/Responses.hs+14−15 src/Data/OpenApi/Compare/Validate/Schema.hs → src/OpenAPI/Checker/Validate/Schema.hs+116−96 src/Data/OpenApi/Compare/Validate/Schema/DNF.hs → src/OpenAPI/Checker/Validate/Schema/DNF.hs+9−9 src/Data/OpenApi/Compare/Validate/Schema/Issues.hs → src/OpenAPI/Checker/Validate/Schema/Issues.hs+39−121 src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hs → src/OpenAPI/Checker/Validate/Schema/JsonFormula.hs+53−61 src/Data/OpenApi/Compare/Validate/Schema/Partition.hs → src/OpenAPI/Checker/Validate/Schema/Partition.hs+38−42 src/Data/OpenApi/Compare/Validate/Schema/Process.hs → src/OpenAPI/Checker/Validate/Schema/Process.hs+61−60 src/Data/OpenApi/Compare/Validate/Schema/Traced.hs → src/OpenAPI/Checker/Validate/Schema/Traced.hs+14−14 src/Data/OpenApi/Compare/Validate/Schema/TypedJson.hs → src/OpenAPI/Checker/Validate/Schema/TypedJson.hs+24−26 src/Data/OpenApi/Compare/Validate/SecurityRequirement.hs → src/OpenAPI/Checker/Validate/SecurityRequirement.hs+13−14 src/Data/OpenApi/Compare/Validate/SecurityScheme.hs → src/OpenAPI/Checker/Validate/SecurityScheme.hs+5−5 src/Data/OpenApi/Compare/Validate/Server.hs → src/OpenAPI/Checker/Validate/Server.hs+18−21 src/OpenAPI/Checker/Validate/Sums.hs+24 stack.yaml+3−18 stack.yaml.lock+4−25 test/README.md+1−1 test/Spec/Golden/Extra.hs+44−48 test/Spec/Golden/TraceTree.hs+6−7 test/golden/common/bad-oneof-unchanged/report.md+6−6 test/golden/common/bad-oneof-unchanged/trace-tree.yaml+2−2 test/golden/common/enum-added/report.md+7−7 test/golden/common/enum-added/trace-tree.yaml+2−2 test/golden/common/enum-anyof/report.md+3−3 test/golden/common/enum-anyof/trace-tree.yaml+2−2 test/golden/common/id/report.md+3−3 test/golden/common/id/trace-tree.yaml+2−2 test/golden/common/json/recursive/report.md+3−3 test/golden/common/json/recursive/trace-tree.yaml+2−2 test/golden/common/maximum-lowered/report.md+7−7 test/golden/common/maximum-lowered/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/report.md+4−4 test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/report.md+4−4 test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/change/report.md+5−5 test/golden/common/pathItem/operation/parameters/change/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/required/false/add/report.md+3−3 test/golden/common/pathItem/operation/parameters/required/false/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/required/false/del/report.md+3−3 test/golden/common/pathItem/operation/parameters/required/false/del/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/required/reset/report.md+4−4 test/golden/common/pathItem/operation/parameters/required/reset/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/required/set/report.md+4−4 test/golden/common/pathItem/operation/parameters/required/set/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/required/true/add/report.md+4−4 test/golden/common/pathItem/operation/parameters/required/true/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/parameters/required/true/del/report.md+4−4 test/golden/common/pathItem/operation/parameters/required/true/del/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/report.md+4−4 test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/report.md+7−7 test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/report.md+4−4 test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/requestBody/required/reset/report.md+4−4 test/golden/common/pathItem/operation/requestBody/required/reset/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/requestBody/required/set/report.md+4−4 test/golden/common/pathItem/operation/requestBody/required/set/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/add/report.md+4−4 test/golden/common/pathItem/operation/responses/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/report.md+4−4 test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/report.md+4−4 test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/headers/optional/add/report.md+3−3 test/golden/common/pathItem/operation/responses/change/headers/optional/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/headers/optional/del/report.md+3−3 test/golden/common/pathItem/operation/responses/change/headers/optional/del/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/report.md+4−4 test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/report.md+7−7 test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/report.md+4−4 test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/trace-tree.yaml+2−2 test/golden/common/pathItem/operation/responses/del/report.md+4−4 test/golden/common/pathItem/operation/responses/del/trace-tree.yaml+2−2 test/golden/common/property-removed-additional/report.md+7−7 test/golden/common/property-removed-additional/trace-tree.yaml+2−2 test/golden/common/property-removed/report.md+22−6 test/golden/common/property-removed/trace-tree.yaml+2−2 test/golden/common/property-required/report.md+7−7 test/golden/common/property-required/trace-tree.yaml+2−2 test/golden/common/random-example/a.yaml−56 test/golden/common/random-example/b.yaml−58 test/golden/common/random-example/report.md−71 test/golden/common/random-example/trace-tree.yaml−48 test/golden/common/recursive/report.md+7−7 test/golden/common/recursive/trace-tree.yaml+2−2 test/golden/common/security-scheme/report.md+5−5 test/golden/common/security-scheme/trace-tree.yaml+2−2 test/golden/common/servers/report.md+5−5 test/golden/common/servers/trace-tree.yaml+2−2 test/golden/common/tuple-items/heterogeneous-component-mismatch/report.md+9−9 test/golden/common/tuple-items/heterogeneous-component-mismatch/trace-tree.yaml+2−2 test/golden/common/tuple-items/heterogeneous-length-mismatch/report.md+22−6 test/golden/common/tuple-items/heterogeneous-length-mismatch/trace-tree.yaml+2−2 test/golden/common/tuple-items/homogeneous-component-mismatch/report.md+17−9 test/golden/common/tuple-items/homogeneous-component-mismatch/trace-tree.yaml+2−2 test/golden/common/tuple-items/homogeneous-length-mismatch/report.md+15−15 test/golden/common/tuple-items/homogeneous-length-mismatch/trace-tree.yaml+2−2 test/golden/common/tuple-items/homogeneous-vector/report.md+3−3 test/golden/common/tuple-items/homogeneous-vector/trace-tree.yaml+2−2 test/golden/common/type-changing/report.md+15−15 test/golden/common/type-changing/trace-tree.yaml+2−2 test/golden/common/unguarded-recursive/basic/a.yaml → test/golden/common/unguarded-recursive/a.yaml test/golden/common/unguarded-recursive/basic/b.yaml → test/golden/common/unguarded-recursive/b.yaml test/golden/common/unguarded-recursive/basic/report.md−13 test/golden/common/unguarded-recursive/report.md+13 test/golden/common/unguarded-recursive/silent/a.yaml−25 test/golden/common/unguarded-recursive/silent/b.yaml−19 test/golden/common/unguarded-recursive/silent/report.md−15 test/golden/common/unguarded-recursive/silent/trace-tree.yaml−16 test/golden/common/unguarded-recursive/basic/trace-tree.yaml → test/golden/common/unguarded-recursive/trace-tree.yaml+2−2 test/golden/common/variant-added/report.md+7−7 test/golden/common/variant-added/trace-tree.yaml+2−2 test/golden/common/variant-changed/report.md+9−9 test/golden/common/variant-changed/trace-tree.yaml+2−2 test/golden/common/variant-deep-changed/report.md+9−9 test/golden/common/variant-deep-changed/trace-tree.yaml+2−2
.github/actions/common_setup/action.yamldeleted−42
@@ -1,42 +0,0 @@
1−name: "Common setup"
2−description: "Common setup steps"
3−inputs:
4− authToken:
5− description: "Cachix auth token"
6− required: true
7−runs:
8− using: "composite"
9− steps:
10− - name: Increase swap if linux
11− shell: bash
12− run: |
13− if [ "$RUNNER_OS" == "Linux" ]; then
14−
15− echo "Is Linux"
16−
17− free -h
18−
19− sudo swapoff /mnt/swapfile
20− sudo fallocate -l 12G /mnt/swapfile
21− sudo mkswap /mnt/swapfile
22− sudo swapon /mnt/swapfile
23−
24− free -h
25−
26− else
27−
28− echo "Not Linux"
29−
30− fi
31−
32− - name: Install Nix
33− uses: cachix/install-nix-action@v27
34− with:
35− extra_nix_config: |
36− system-features = kvm
37− trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
38− substituters = https://hydra.iohk.io https://cache.nixos.org/
39− - uses: cachix/cachix-action@v15
40− with:
41− name: typeable
42− authToken: "${{ inputs.authToken }}"
.github/workflows/github-actions-image.yamldeleted−290
@@ -1,290 +0,0 @@
1−name: Github Action Docker Image
2−
3−on:
4− push:
5− branches: [master]
6− pull_request:
7− branches: [master]
8− release:
9− types: [prereleased]
10−
11−jobs:
12−# check-formatting:
13−# name: Check Haskell formatting
14−# runs-on: ubuntu-latest
15−# steps:
16−# - uses: actions/checkout@v2
17−#
18−# - name: Common setup
19−# uses: ./.github/actions/common_setup
20−# with:
21−# authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
22−#
23−# - name: Run fourmolu
24−# run: |
25−# nix-shell nix/ci.nix -j auto --run "git ls-files '*.hs' | xargs fourmolu -m inplace -o -XRecursiveDo -o -XTypeApplications -o -XPatternSynonyms -o -XBangPatterns"
26−# git diff --exit-code
27−
28− check_versions:
29− runs-on: ubuntu-latest
30− steps:
31− - uses: actions/checkout@v2
32− with:
33− submodules: recursive
34−
35− - name: Common setup
36− uses: ./.github/actions/common_setup
37− with:
38− authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
39−
40− - name: Check library version
41− if: github.event_name == 'release'
42− run: |
43− if [ "$(awk -F ':' '/^version/ {gsub(/ /, "", $2); print $2}' compaREST.cabal)" != "${{ github.event.release.tag_name }}" ]; then
44− echo "::error file=compaREST.cabal::Library version does not match release version."
45− exit 1
46− fi
47−
48− - name: Check docker tag
49− if: github.event_name == 'release'
50− run: |
51− if [ "$(yq eval '.runs.image' action.yaml | awk -F ':' '{print $NF}')" != "${{ github.event.release.tag_name }}" ]; then
52− echo "::error file=action.yaml::Action docker tag does not match release tag."
53− exit 1
54− fi
55−
56− - name: Check changelog
57− if: github.event_name == 'release'
58− run: |
59− if ! grep '^## ${{ github.event.release.tag_name }}$' CHANGELOG.md; then
60− echo "::error file=CHANGELOG.md::CHANGELOG.md does not contain an entry for this release."
61− exit 1
62− fi
63−
64− Linux:
65− needs: check_versions
66− runs-on: ubuntu-latest
67− steps:
68− - uses: actions/checkout@v2
69− with:
70− submodules: recursive
71−
72− - name: Common setup
73− uses: ./.github/actions/common_setup
74− with:
75− authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
76−
77− - name: Test
78− shell: bash
79− run: |
80− $(nix-build -A test -j auto --no-out-link)/bin/compaREST-tests
81−
82− - name: Build Linux static binary
83− shell: bash
84− run: |
85− cp $(nix-build -A compaRESTStaticBin -j auto --no-out-link)/bin/compaREST compaREST
86−
87− - name: Upload compaREST Linux Binary
88− uses: actions/upload-artifact@v2
89− with:
90− name: compaREST-Linux
91− path: compaREST
92−
93− - name: Build Docker Images
94− shell: bash
95− run: |
96− cp $(nix-build -A compaRESTGithubAction -j auto --no-out-link) compaREST-Github-Action-image.tar.gz
97− cp $(nix-build -A compaRESTImage -j auto --no-out-link) compaREST-image.tar.gz
98−
99− - name: Upload compaREST Docker Github Action Image
100− uses: actions/upload-artifact@v2
101− with:
102− name: compaREST Github Action Docker Image
103− path: compaREST-Github-Action-image.tar.gz
104−
105− - name: Upload compaREST Docker Image
106− uses: actions/upload-artifact@v2
107− with:
108− name: compaREST Docker Image
109− path: compaREST-image.tar.gz
110−
111− - name: Login to DockerHub
112− uses: docker/login-action@v1.10.0
113− with:
114− username: ${{ secrets.DOCKER_HUB_USERNAME }}
115− password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
116−
117− - name: Push Nightly Docker Images to DockerHub
118− if: github.ref == 'refs/heads/master' && github.event_name == 'push'
119− shell: bash
120− run: |
121− skopeo copy docker-archive:$(nix-build -A compaRESTGithubAction -j auto --no-out-link) docker://typeable/comparest-github-action:nightly
122− skopeo copy docker-archive:$(nix-build -A compaRESTImage -j auto --no-out-link) docker://typeable/comparest:nightly
123−
124− - name: Push Release Docker Images to DockerHub
125− if: github.event_name == 'release'
126− shell: bash
127− run: |
128− skopeo copy docker-archive:$(nix-build -A compaRESTGithubAction -j auto --no-out-link) docker://typeable/comparest-github-action:${{ github.event.release.tag_name }}
129− skopeo copy docker-archive:$(nix-build -A compaRESTImage -j auto --no-out-link) docker://typeable/comparest:${{ github.event.release.tag_name }}
130−
131− - name: Zip Release Assets
132− if: github.event_name == 'release'
133− shell: bash
134− run: |
135− zip compaREST-Linux-x86.zip compaREST
136−
137− - name: Upload compaREST Github Action Docker Image to release
138− if: github.event_name == 'release'
139− uses: svenstaro/upload-release-action@2.2.1
140− with:
141− repo_token: ${{ secrets.GITHUB_TOKEN }}
142− file: compaREST-Github-Action-image.tar.gz
143− tag: ${{ github.ref }}
144−
145− - name: Upload compaREST Docker Image to release
146− if: github.event_name == 'release'
147− uses: svenstaro/upload-release-action@2.2.1
148− with:
149− repo_token: ${{ secrets.GITHUB_TOKEN }}
150− file: compaREST-image.tar.gz
151− tag: ${{ github.ref }}
152−
153− - name: Upload compaREST binary – Linux – x86 to release
154− if: github.event_name == 'release'
155− uses: svenstaro/upload-release-action@2.2.1
156− with:
157− repo_token: ${{ secrets.GITHUB_TOKEN }}
158− file: compaREST-Linux-x86.zip
159− tag: ${{ github.ref }}
160−
161− dist:
162− needs: [check_versions, Linux]
163− runs-on: ubuntu-latest
164− steps:
165− - uses: actions/checkout@v2
166− with:
167− submodules: recursive
168−
169− - name: Common setup
170− uses: ./.github/actions/common_setup
171− with:
172− authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
173−
174− - name: Cache
175− uses: actions/cache@v1
176− with:
177− path: |
178− ~/.stack
179− .stack-work
180− key: ${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}-${{ hashFiles('**.cabal') }}
181− restore-keys: |
182− ${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}
183− ${{ runner.os }}-stack
184−
185− - name: Upgrade stack
186− shell: bash
187− # Needed for Hackage token authorization
188− run: stack upgrade --git
189−
190− - name: Build dist
191− run: |
192− stack sdist --pvp-bounds both
193− echo "distPath=$(stack path | awk -F ": " '/dist-dir/ {print $2}')" >> $GITHUB_ENV
194−
195− - name: Upload compaREST sources artifact
196− uses: actions/upload-artifact@v2
197− with:
198− name: compaREST sources
199− path: ${{ env.distPath }}/*.tar.gz
200−
201− - name: Upload compaREST sources to release
202− if: github.event_name == 'release'
203− uses: svenstaro/upload-release-action@2.2.1
204− with:
205− repo_token: ${{ secrets.GITHUB_TOKEN }}
206− file_glob: true
207− file: ${{ env.distPath }}/*.tar.gz
208− tag: ${{ github.ref }}
209−
210− - name: Publish compaREST candidate to Hackage
211− shell: bash
212− if: github.event_name == 'release'
213− env:
214− HACKAGE_KEY: ${{ secrets.HACKAGE_KEY }}
215− run: |
216− stack upload --pvp-bounds both --candidate .
217−
218− macOS:
219− needs: check_versions
220− runs-on: macos-latest
221− steps:
222− - uses: actions/checkout@v2
223− with:
224− submodules: recursive
225−
226− - name: Common setup
227− uses: ./.github/actions/common_setup
228− with:
229− authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
230−
231− - name: Build macOS binary bundle
232− run: |
233− cp -LR $(nix-build -A macOSCompaRESTBundle -j auto --no-out-link) compaREST
234−
235− - name: Upload macOS compaREST bundle
236− uses: actions/upload-artifact@v2
237− with:
238− name: compaREST binary – macOS – x86
239− path: compaREST
240−
241− - name: Zip Release Assets
242− if: github.event_name == 'release'
243− shell: bash
244− run: |
245− zip -r compaREST-macOS-x86.zip compaREST
246−
247− - name: Upload compaREST binary – macOS – x86 to release
248− if: github.event_name == 'release'
249− uses: svenstaro/upload-release-action@2.2.1
250− with:
251− repo_token: ${{ secrets.GITHUB_TOKEN }}
252− file: compaREST-macOS-x86.zip
253− tag: ${{ github.ref }}
254−
255−# Linux-Windows:
256−# needs: check_versions
257−# runs-on: ubuntu-latest
258−# steps:
259−# - uses: actions/checkout@v2
260−# with:
261−# submodules: recursive
262−#
263−# - name: Common setup
264−# uses: ./.github/actions/common_setup
265−# with:
266−# authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
267−#
268−# - name: Build Windows binary
269−# run: |
270−# cp -LR $(nix-build -A WindowsCompaRESTBin -j auto --no-out-link)/bin compaREST
271−#
272−# - name: Upload Windows compaREST bundle
273−# uses: actions/upload-artifact@v2
274−# with:
275−# name: compaREST binary – Windows
276−# path: compaREST
277−#
278−# - name: Zip Release Assets
279−# if: github.event_name == 'release'
280−# shell: bash
281−# run: |
282−# zip -r compaREST-Windows.zip compaREST
283−#
284−# - name: Upload compaREST binary – Windows to release
285−# if: github.event_name == 'release'
286−# uses: svenstaro/upload-release-action@2.2.1
287−# with:
288−# repo_token: ${{ secrets.GITHUB_TOKEN }}
289−# file: compaREST-Windows.zip
290−# tag: ${{ github.ref }}
.github/workflows/haskell.ymlnew file+54
@@ -0,0 +1,54 @@
1+name: Haskell CI
2+
3+on:
4+ push:
5+ branches: [master]
6+ pull_request:
7+ branches: [master]
8+
9+jobs:
10+ linux-build:
11+ runs-on: ubuntu-latest
12+
13+ steps:
14+ - uses: actions/checkout@v2
15+ with:
16+ submodules: recursive
17+ - name: Cache
18+ uses: actions/cache@v1
19+ with:
20+ path: ~/.stack
21+ key: ${{ runner.os }}-build-stack-${{ hashFiles('**/stack.yaml') }}-${{ hashFiles('**/*.cabal') }}
22+ restore-keys: |
23+ ${{ runner.os }}-build-stack-${{ hashFiles('**/stack.yaml') }}
24+ ${{ runner.os }}-build-stack-
25+ - name: Increase swap
26+ run: |
27+ free -h
28+
29+ sudo swapoff /mnt/swapfile
30+ sudo fallocate -l 12G /mnt/swapfile
31+ sudo mkswap /mnt/swapfile
32+ sudo swapon /mnt/swapfile
33+
34+ free -h
35+
36+ - name: Build
37+ run: |
38+ stack build --ghc-options=-O3 --local-bin-path openapi-diff-linux --copy-bins --flag openapi-diff:Static
39+ - name: Test
40+ run: stack test --ghc-options=-O3 --flag openapi-diff:Static --test-arguments='--hide-successes --no-create'
41+
42+ - name: Zip
43+ if: github.ref == 'refs/heads/master'
44+ run: |
45+ zip openapi-diff-linux openapi-diff-linux/openapi-diff
46+ - uses: "marvinpinto/action-automatic-releases@latest"
47+ if: github.ref == 'refs/heads/master'
48+ with:
49+ repo_token: "${{ secrets.GITHUB_TOKEN }}"
50+ automatic_release_tag: "latest"
51+ prerelease: false
52+ title: "Pre-Release"
53+ files: |
54+ *.zip
.github/workflows/remove-artifacts.yamlnew file+17
@@ -0,0 +1,17 @@
1+name: Remove old artifacts
2+
3+on:
4+ schedule:
5+ - cron: '0 0 * * *'
6+jobs:
7+ remove-old-artifacts:
8+ runs-on: ubuntu-latest
9+ timeout-minutes: 10
10+
11+ steps:
12+ - name: Remove old artifacts
13+ uses: c-hive/gha-remove-artifacts@v1
14+ with:
15+ age: '7 days'
16+ skip-tags: true
17+ skip-recent: 10
.gitmodulesnew file+3
@@ -0,0 +1,3 @@
1+[submodule "awsm-css"]
2+ path = awsm-css
3+ url = https://github.com/igoradamenko/awsm.css.git
CHANGELOG.md+3−7
@@ -1,13 +1,9 @@
11# Changelog
22
3−`compaREST` uses [PVP Versioning][1].
3+`openapi-diff` uses [PVP Versioning][1].
44
5−## 0.1.0.1
5+## 0.0.0.0
66
7−* Fixed an issue related to empty commit SHAs in GitHub Actions.
8−
9−## 0.1.0.0
10−
11−* Initial release.
7+* Initially created.
128
139[1]: https://pvp.haskell.org
LICENSE+1−1
@@ -1,6 +1,6 @@
11MIT License
22
3−Copyright (c) 2021 Typeable
3+Copyright (c) 2021 Aleksey Uymanov
44
55Permission is hereby granted, free of charge, to any person obtaining a copy
66of this software and associated documentation files (the "Software"), to deal
README.md+4−326
@@ -1,330 +1,8 @@
1−# compaREST
1+# openapi-diff
22
3−[![Hackage](https://img.shields.io/hackage/v/compaREST.svg?logo=haskell)](https://hackage.haskell.org/package/compaREST)
3+[![Hackage](https://img.shields.io/hackage/v/openapi-diff.svg?logo=haskell)](https://hackage.haskell.org/package/openapi-diff)
4+[![Stackage Lts](http://stackage.org/package/openapi-diff/badge/lts)](http://stackage.org/lts/package/openapi-diff)
5+[![Stackage Nightly](http://stackage.org/package/openapi-diff/badge/nightly)](http://stackage.org/nightly/package/openapi-diff)
46[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
57
68Compatibility checker for OpenAPI
7−
8−## Using compaREST in Github Actions
9−
10−Add the following step to your Github Actions workflow:
11−
12−```yaml
13−- uses: typeable/comparest
14− if: ${{ github.event_name == 'pull_request' }}
15− with:
16− old: old-openapi.yaml
17− new: new-openapi.yaml
18−```
19−
20−The `new` and `old` values should be paths to your OpenAPI specifications you want to compare.
21−
22−You will get something like this in your pull requests:
23−
24−![](docs/img/github-action-report.png)
25−
26−For more detail please see our [integration guide](docs/Integration_guide.md).
27−
28−## An example
29−
30−### Your situation
31−
32−You are developing a very important server with a REST API. You have clients who use your API that you do not control. Say, you are also developing a mobile app that uses your API and you can't force someone to update to the latest version. (Or you prefer not to for UX reasons.)
33−
34−You have recently released version 1.0 and things are going great: user are downloading your app, servers are processing requests.
35−
36−You describe your API in a file `api-1.0.0.yaml`:
37−
38−```yaml
39−openapi: "3.0.0"
40−info:
41− version: 1.0.0
42− title: Swagger Petstore
43− license:
44− name: MIT
45−servers:
46− - url: https://example.com
47−paths:
48− /pets:
49− get:
50− parameters:
51− - name: limit
52− in: query
53− required: false
54− schema:
55− type: integer
56− maximum: 20
57− responses:
58− "200":
59− description: ""
60− headers:
61− x-next:
62− schema:
63− type: string
64− content:
65− application/json:
66− schema:
67− $ref: "#/components/schemas/Pets"
68− post:
69− requestBody:
70− content:
71− application/json:
72− schema:
73− $ref: "#/components/schemas/Pet"
74− responses:
75− "201":
76− description: ""
77−components:
78− schemas:
79− Pet:
80− type: object
81− required:
82− - id
83− - name
84− properties:
85− id:
86− type: integer
87− name:
88− type: string
89− minLength: 3
90− maxLength: 10
91− Pets:
92− type: array
93− items:
94− $ref: "#/components/schemas/Pet"
95−```
96−
97−### Evolving your product
98−
99−Enthused over your initial success you hurry to release a new and improved version of your API and mobile app.
100−
101−After a round of very intense programming you take a look at your new `api-1.1.0.yaml`:
102−
103−```yaml
104−openapi: "3.0.0"
105−info:
106− version: 1.1.0
107− title: Swagger Petstore
108− license:
109− name: MIT
110−servers:
111− - url: https://example.com
112−paths:
113− /pets:
114− get:
115− parameters:
116− - name: limit
117− in: query
118− required: false
119− schema:
120− type: integer
121− maximum: 30
122− responses:
123− "200":
124− description: ""
125− headers:
126− x-next:
127− schema:
128− type: string
129− content:
130− application/json:
131− schema:
132− $ref: "#/components/schemas/Pets"
133− post:
134− requestBody:
135− content:
136− application/json:
137− schema:
138− $ref: "#/components/schemas/Pet"
139− responses:
140− "201":
141− description: ""
142−components:
143− schemas:
144− Pet:
145− type: object
146− required:
147− - id
148− - name
149− properties:
150− id:
151− type: integer
152− name:
153− type: string
154− minLength: 1
155− maxLength: 15
156− weight:
157− type: integer
158− Pets:
159− type: array
160− items:
161− $ref: "#/components/schemas/Pet"
162−```
163−
164−Looking at the very large and complex API description, you grow more and more concerned that your old mobile app might stop working when you update the server. But the spec is too large and too complex to reasonably assess this manually.
165−
166−### Assessing compatibility automatically
167−
168−Luckily, you have access to compaREST which can programmatically analyze your APIs and determine what, if anything, breaks compatibility and what doesn't.
169−
170−You can call it, passing the API your client will be aware of, and the API your server will serve like so:
171−
172−```bash
173−docker run --rm -v $(pwd):/data:rw typeable/comparest --client /data/api-1.0.0.yaml --server /data/api-1.1.0.yaml --output /data/report.md
174−```
175−
176−Running this command will output a file `report.md`, containing the compatibility report between the two APIs:
177−
178−> # Summary
179−>
180−> | [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
181−> |------------------------------------------|--------------------------------------------------|
182−> | 5 | 6 |
183−>
184−> # <span id="breaking-changes"></span>❌ Breaking changes
185−>
186−> ## **GET** /pets
187−>
188−> ### ⬅️☁️ JSON Response – 200
189−>
190−> #### `$[*].name(String)`
191−>
192−> 1. Maximum length of the string changed from 10 to 15.
193−>
194−> 2. Minimum length of the string changed from 3 to 1.
195−>
196−> ## **POST** /pets
197−>
198−> ### ➡️☁️ JSON Request
199−>
200−> #### `$.weight`
201−>
202−> 1. Values are now limited to the following types:
203−>
204−> - Number
205−>
206−> 2. The property was previously implicitly described by the catch-all
207−> "additional properties" case. It is now explicitly defined.
208−>
209−> #### `$.weight(Number)`
210−>
211−> Value is now a multiple of 1.0.
212−>
213−> # <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
214−>
215−> ## **GET** /pets
216−>
217−> ### Parameter limit
218−>
219−> #### JSON Schema
220−>
221−> ##### `$(Number)`
222−>
223−> Upper bound changed from 20.0 inclusive to 30.0 inclusive.
224−>
225−> ### ⬅️☁️ JSON Response – 200
226−>
227−> #### `$[*].weight`
228−>
229−> 1. Values are now limited to the following types:
230−>
231−> - Number
232−>
233−> 2. The property was previously implicitly described by the catch-all
234−> "additional properties" case. It is now explicitly defined.
235−>
236−> #### `$[*].weight(Number)`
237−>
238−> Value is now a multiple of 1.0.
239−>
240−> ## **POST** /pets
241−>
242−> ### ➡️☁️ JSON Request
243−>
244−> #### `$.name(String)`
245−>
246−> 1. Maximum length of the string changed from 10 to 15.
247−>
248−> 2. Minimum length of the string changed from 3 to 1.
249−
250−You now know exactly in what situations and in what way your 1.0 version of the app will break if you deploy your 1.1 version of the server.
251−
252−### Additional formats
253−
254−You can also produce a self-contained HTML report that you can open in your browser by simply omitting the file extension of the output file:
255−
256−```bash
257−docker run --rm -v $(pwd):/data:rw typeable/comparest --client /data/api-1.0.0.yaml --server /data/api-1.1.0.yaml --output /data/report
258−```
259−
260−## CLI docs
261−
262−For more detail please see our [user guide](docs/User_guide.md).
263−
264−```
265−Usage: comparest (-c|--client ARG) (-s|--server ARG)
266− [--silent | --only-breaking | --all] [-o|--output ARG]
267− [--folding-block-quotes-style | --header-style]
268− [--signal-exit-code]
269− A tool to check compatibility between two OpenAPI specifications.
270−
271− Usage examples
272−
273− Compare files old.yaml with new.yaml and output the resulting report to
274− stdout:
275−
276− comparest -c old.yaml -s new.yaml
277−
278− Only output breaking changes and write a styled HTML report to file
279− report.html:
280−
281− comparest -c old.yaml -s new.yaml --only-breaking -o report
282−
283− Don't output anything, only fail if there are breaking changes:
284−
285− comparest -c old.json -s new.json --silent
286−
287− Write full report suitable for embedding into a GitHub comment to
288− report.html:
289−
290− comparest -c old.json -s new.json --folding-block-quotes-style -o report.html
291−
292−Available options:
293− -h,--help Show this help text
294− -c,--client ARG A path to the file containing the specification that
295− will be used for the client of the API. Can be either
296− a YAML or JSON file.
297− -s,--server ARG A path to the file containing the specification that
298− will be used for the server of the API. Can be either
299− a YAML or JSON file.
300− --silent Silence all output. Only makes sense in combination
301− with --signal-exit-code.
302− --only-breaking Only report breaking changes in the output.
303− --all Report both incompatible and compatible changes.
304− Compatible changes will not trigger a failure exit
305− code.
306− -o,--output ARG The file path where the output should be written. If
307− the option is omitted the result will be written to
308− stdout.
309−
310− The file extension is used to determine the type of
311− the output file.
312−
313− Supports many formats such as markdown, html, rtf,
314− doc, txt, rst, and many more.
315−
316− Leave out the extension to produce a self-contained
317− HTML report with styling.
318− --folding-block-quotes-style
319− The report tree is structured using summary/detail
320− HTML elements and indented using block quotes. This
321− style renders well on GitHub.Intended for HTML output
322− format. Markdown has rendering bugs on GitHub.
323− --header-style The report tree is structured using increasing levels
324− of headers.
325− --signal-exit-code Signal API compatibility with the exit code.
326−
327− Exit with 0 if there are no breaking changes.
328− Exit with 1 if there are breaking changes.
329− Exit with 2 if could not determine compatibility.
330−```
action.yamldeleted−48
@@ -1,48 +0,0 @@
1−name: compaREST
2−author: Typeable
3−description: API reliability checker
4−inputs:
5− GITHUB_TOKEN:
6− description: "The GitHub access token (e.g. secrets.GITHUB_TOKEN) used to create or update the comment. This defaults to {{ github.token }}."
7− default: "${{ github.token }}"
8− required: false
9− repo:
10− description: The owner of the repo in which to post the results.
11− default: "${{ github.repository }}"
12− required: false
13− project_name:
14− description: The name of the project to which the API pertains. Used to name the check. Useful when you have multiple APIs in the same repo.
15− required: false
16− default: ""
17− footer:
18− description: A footer that can be appended to the check body.
19− required: false
20− default: ""
21− old:
22− description: The path to the old (client) specification of the API.
23− required: true
24− new:
25− description: The path to new (server) specification of the API.
26− required: true
27− sha:
28− description: The sha of the commit to post the check for.
29− required: false
30− default: "${{ github.event.pull_request.head.sha }}"
31−runs:
32− using: "docker"
33− image: "docker://typeable/comparest-github-action:0.1.0.1"
34− env:
35− GITHUB_TOKEN: "${{ inputs.GITHUB_TOKEN }}"
36− REPO: "${{ inputs.repo }}"
37− PROJECT_NAME: "${{ inputs.project_name }}"
38− FOOTER: "${{ inputs.footer }}"
39− ROOT: "/github/workspace"
40− OLD: "${{ inputs.old }}"
41− NEW: "${{ inputs.new }}"
42− SHA: "${{ inputs.sha }}"
43− pre-entrypoint: "/bin/pre"
44− entrypoint: "/bin/run"
45−
46−branding:
47− icon: crosshair
48− color: blue
app/Data/OpenApi/Compare/Options.hsdeleted−135
@@ -1,135 +0,0 @@
1−module Data.OpenApi.Compare.Options
2− ( Options (..),
3− OutputMode (..),
4− parseOptions,
5− )
6−where
7−
8−import Data.OpenApi.Compare.Report
9−import GHC.Generics (Generic)
10−import Options.Applicative
11−import Options.Applicative.Help hiding (fullDesc)
12−import Text.PrettyPrint.ANSI.Leijen ((<$$>),string)
13−
14−parseOptions :: IO Options
15−parseOptions = customExecParser (prefs $ showHelpOnError) optionsParserInfo
16−
17−data Options = Options
18− { clientFile :: FilePath
19− , serverFile :: FilePath
20− , -- | 'Nothing' means "don't produce any output"
21− mode :: Maybe ReportMode
22− , outputMode :: OutputMode
23− , reportTreeStyle :: ReportTreeStyle
24− , signalExitCode :: Bool
25− }
26− deriving stock (Generic)
27−
28−data OutputMode = StdoutMode | FileMode FilePath
29−
30−optionsParserInfo :: ParserInfo Options
31−optionsParserInfo =
32− info
33− (helper <*> optionsParser)
34− ( fullDesc
35− <> header "CompaREST"
36− <> progDescDoc
37− ( Just $
38− par "A tool to check compatibility between two OpenAPI specifications."
39− <$$> hardline <> par "Usage examples" <> hardline
40− <$$> indent
41− 4
42− ( par "Compare files old.yaml with new.yaml and output the resulting report to stdout:"
43− <$$> hardline <> indent 4 "comparest -c old.yaml -s new.yaml"
44− <$$> hardline <> par "Only output breaking changes and write a styled HTML report to file report.html:"
45− <$$> hardline <> indent 4 "comparest -c old.yaml -s new.yaml --only-breaking -o report"
46− <$$> hardline <> par "Don't output anything, only fail if there are breaking changes:"
47− <$$> hardline <> indent 4 "comparest -c old.json -s new.json --silent"
48− <$$> hardline <> par "Write full report suitable for embedding into a GitHub comment to report.html:"
49− <$$> hardline <> indent 4 "comparest -c old.json -s new.json --folding-block-quotes-style -o report.html"
50− )
51− )
52− )
53−
54−optionsParser :: Parser Options
55−optionsParser =
56− Options
57− <$> strOption
58− ( short 'c'
59− <> long "client"
60− <> help
61− "A path to the file containing the specification that will be \
62− \used for the client of the API. Can be either a YAML or JSON file."
63− )
64− <*> strOption
65− ( short 's'
66− <> long "server"
67− <> help
68− "A path to the file containing the specification that will be \
69− \used for the server of the API. Can be either a YAML or JSON file."
70− )
71− <*> ( flag'
72− Nothing
73− ( long "silent"
74− <> help "Silence all output. Only makes sense in combination with --signal-exit-code."
75− )
76− <|> flag'
77− (Just OnlyErrors)
78− ( long "only-breaking"
79− <> help "Only report breaking changes in the output."
80− )
81− <|> flag'
82− (Just All)
83− ( long "all"
84− <> help
85− "Report both incompatible and compatible changes. \
86− \Compatible changes will not trigger a failure exit code."
87− )
88− <|> pure (Just All)
89− )
90− <*> ( ( FileMode
91− <$> strOption
92− ( short 'o' <> long "output"
93− <> helpDoc
94− ( Just $
95− par "The file path where the output should be written. If the option is omitted the result will be written to stdout."
96− <$$> hardline <> par "The file extension is used to determine the type of the output file."
97− <$$> hardline <> par "Supports many formats such as markdown, html, rtf, doc, txt, rst, and many more."
98− <$$> hardline <> par "Leave out the extension to produce a self-contained HTML report with styling."
99− )
100− )
101− )
102− <|> pure StdoutMode
103− )
104− <*> ( flag'
105− FoldingBlockquotesTreeStyle
106− ( long "folding-block-quotes-style"
107− <> help
108− "The report tree is structured using \
109− \summary/detail HTML elements and indented using \
110− \block quotes. This style renders well on GitHub.\
111− \Intended for HTML output format. Markdown has rendering \
112− \bugs on GitHub."
113− )
114− <|> flag'
115− HeadersTreeStyle
116− ( long "header-style"
117− <> help
118− "The report tree is structured using \
119− \increasing levels of headers."
120− )
121− <|> pure HeadersTreeStyle
122− )
123− <*> switch
124− ( long "signal-exit-code"
125− <> helpDoc
126− ( Just $
127− par "Signal API compatibility with the exit code."
128− <$$> hardline <> par "Exit with 0 if there are no breaking changes."
129− <$$> par "Exit with 1 if there are breaking changes."
130− <$$> par "Exit with 2 if could not determine compatibility."
131− )
132− )
133−
134−par :: String -> Doc
135−par = foldr1 (</>) . fmap string . words
app/FormatHeuristic.hs+3−3
@@ -2,7 +2,7 @@
22-- Originally based on:
33-- https://github.com/jgm/pandoc/blob/master/src/Text/Pandoc/App/FormatHeuristics.hs
44module FormatHeuristic
5− ( formatFromFilePath,
5+ ( formatFromFilePath
66 )
77where
88
@@ -10,10 +10,10 @@ import Data.ByteString.Lazy (ByteString)
1010import Data.Char (toLower)
1111import Data.Functor
1212import qualified Data.Map as M
13−import qualified Data.OpenApi.Compare.Report.Html.Template as Html
1413import Data.Text (Text)
1514import qualified Data.Text.Lazy as TL
1615import qualified Data.Text.Lazy.Encoding as TL
16+import qualified OpenAPI.Checker.Report.Html.Template as Html
1717import System.FilePath (takeExtension)
1818import Text.DocTemplates.Internal
1919import Text.Pandoc
@@ -80,7 +80,7 @@ formatFromFilePath x =
8080 def
8181 { writerTemplate = Just Html.template
8282 , -- Not actually used. Needed to silence warning.
83− writerVariables = Context $ M.fromList [("pagetitle", toVal ("CompaREST" :: Text))]
83+ writerVariables = Context $ M.fromList [("pagetitle", toVal ("OpenApi Diff" :: Text))]
8484 }
8585 _ -> Nothing
8686 where
app/Main.hs+8−11
@@ -2,24 +2,22 @@ module Main (main) where
22
33import Control.Monad
44import Control.Monad.Except
5−import Control.Monad.Trans (lift)
65import Data.Aeson
76import qualified Data.ByteString.Lazy as BSL
87import Data.Default
98import Data.Maybe
10−import Data.OpenApi.Compare.Options
11−import Data.OpenApi.Compare.Run
129import qualified Data.Text.IO as T
1310import qualified Data.Yaml as Yaml
1411import FormatHeuristic
12+import OpenAPI.Checker.Options
13+import OpenAPI.Checker.Run
1514import System.Exit
1615import System.IO
1716import Text.Pandoc hiding (report)
18−import Text.Pandoc.Builder
1917
2018main :: IO ()
2119main = do
22− opts <- parseOptions
20+ opts <- execParser optionsParserInfo
2321 let parseSchema path =
2422 eitherDecodeFileStrict path >>= \case
2523 Left jsonErr -> do
@@ -49,13 +47,12 @@ main = do
4947 }
5048 (report, status) = runReport reportConfig (a, b)
5149 case mode opts of
52− Just _ -> either handler pure <=< runExceptT $ write $ doc report
50+ Just _ -> either handler pure <=< runExceptT $ write report
5351 Nothing -> pure ()
54− when (signalExitCode opts) $
55− case status of
56− NoBreakingChanges -> exitSuccess
57− BreakingChanges -> exitWith $ ExitFailure 1
58− OnlyUnsupportedChanges -> exitWith $ ExitFailure 2
52+ case status of
53+ NoBreakingChanges -> exitSuccess
54+ BreakingChanges -> exitWith $ ExitFailure 1
55+ OnlyUnsupportedChanges -> exitWith $ ExitFailure 2
5956
6057data Errors
6158 = DocumentError PandocError
app/OpenAPI/Checker/Options.hsnew file+81
@@ -0,0 +1,81 @@
1+module OpenAPI.Checker.Options
2+ ( Options (..)
3+ , OutputMode (..)
4+ , optionsParserInfo
5+ , execParser
6+ )
7+where
8+
9+import GHC.Generics (Generic)
10+import OpenAPI.Checker.Report
11+import Options.Applicative
12+
13+data Options = Options
14+ { clientFile :: FilePath
15+ , serverFile :: FilePath
16+ , -- | 'Nothing' means "don't produce any output"
17+ mode :: Maybe ReportMode
18+ , outputMode :: OutputMode
19+ , reportTreeStyle :: ReportTreeStyle
20+ }
21+ deriving stock (Generic)
22+
23+data OutputMode = StdoutMode | FileMode FilePath
24+
25+optionsParserInfo :: ParserInfo Options
26+optionsParserInfo =
27+ info
28+ (helper <*> optionsParser)
29+ (fullDesc
30+ <> header "openapi-diff"
31+ <> progDesc "A tool to check compatibility between two OpenApi specifications.")
32+
33+optionsParser :: Parser Options
34+optionsParser =
35+ Options
36+ <$> strOption
37+ (short 'c'
38+ <> long "client"
39+ <> help "The specification that will be used for the client of the API.")
40+ <*> strOption
41+ (short 's'
42+ <> long "server"
43+ <> help "The specification that will be used for the server of the API.")
44+ <*> (flag'
45+ Nothing
46+ (long "silent"
47+ <> help "Silence all output.")
48+ <|> flag'
49+ (Just OnlyErrors)
50+ (long "only-errors"
51+ <> help "Only report incompatibility errors in the output.")
52+ <|> flag'
53+ (Just All)
54+ (long "all"
55+ <> help
56+ "Report both incompatible and compatible changes. \
57+ \Compatible changes will not trigger a failure exit code.")
58+ <|> pure (Just All))
59+ <*> ((FileMode
60+ <$> strOption
61+ (short 'o' <> long "output"
62+ <> help
63+ "The file path where the output should be writtrn. \
64+ \Leave blank to output result to stdout."))
65+ <|> pure StdoutMode)
66+ <*> (flag'
67+ FoldingBlockquotesTreeStyle
68+ (long "folding-block-quotes-style"
69+ <> help
70+ "The report tree is structured using \
71+ \summary/detail HTML elements and indented using \
72+ \block quotes. This style renders well on GitHub.\
73+ \Intended for HTML output format. Markdown has rendering \
74+ \bugs on GitHub.")
75+ <|> flag'
76+ HeadersTreeStyle
77+ (long "header-style"
78+ <> help
79+ "The report tree is structured using \
80+ \increasing levels of headers.")
81+ <|> pure HeadersTreeStyle)
awsm-cssnew file+1

Submodule changed.

benchmarks/README.md+2−2
@@ -1,6 +1,6 @@
11# OpenApi comparison benchmarks
22
3−This is a nix expression that automatically downloads, builds and runs multiple OpenAPI comparison tools on the same set of inputs to compare how well they behave.
3+This is a nix expression that automatically downloads, builds and runs multiple OpenApi comparison tools on the same set of inputs to compare how well they behave.
44
55To run the benchmarks run the following command in this directory:
66
@@ -12,7 +12,7 @@ nix-build
1212
1313| Tool | Output file name |
1414| -------------------------------------------------------------------------- | ------------------------------------------- |
15−| [Typeable CompaREST](https://github.com/typeable/compaREST) | `typeable.md` |
15+| [Typeable OpenApi Diff](https://github.com/typeable/openapi-diff) | `typeable.md` |
1616| [Atlassian OpenApi Diff](https://bitbucket.org/atlassian/openapi-diff) | `atlassian.json`, `atlassian.error.txt` |
1717| [OpenApi Tools OpenApi Diff](https://github.com/OpenAPITools/openapi-diff) | `openapitools.md`, `openapitools.error.txt` |
1818| [Tufin OpenApi Diff](https://github.com/tufin/oasdiff) | `oasdiff.txt`, `oasdiff.error.txt` |
benchmarks/default.nix+12−8
@@ -1,6 +1,6 @@
11{ sources ? import ./nix/sources.nix
2+, haskellNix ? import sources.haskellNix { }
23, gomod2nix ? sources.gomod2nix
3−, haskellNix ? (import ../. {}).haskellNix
44, pkgs ? import haskellNix.sources.nixpkgs-2105 (haskellNix.nixpkgsArgs // {
55 overlays = haskellNix.nixpkgsArgs.overlays ++ [
66 (self: super: {
@@ -8,19 +8,23 @@
88 })
99 ];
1010 })
11−, compaRESThsPkgs ? (import ../. {}).hsPkgs
1211, npmNix ? import (sources.npmNix + "/npmPackages") { inherit pkgs; }
1312, mavenix ? import (sources.mavenix) { inherit pkgs; }
1413, bumpToken ? null
1514, bumpDocumentation ? null
1615}:
1716let
18− typeable-comparest = compaRESThsPkgs.comparest.components.exes.comparest;
19− typeable-comparest-exe = typeable-comparest + "/bin/comparest";
20− typeable-comparest-differ = pkgs.writeScript "typeable-comparest-differ" ''
17+ typeable-openapi-diff = (pkgs.haskell-nix.stackProject {
18+ src = pkgs.haskell-nix.haskellLib.cleanGit {
19+ name = "openapi-diff";
20+ src = ./..;
21+ };
22+ }).openapi-diff.components.exes.openapi-diff;
23+ typeable-openapi-diff-exe = typeable-openapi-diff + "/bin/openapi-diff";
24+ typeable-openapi-diff-differ = pkgs.writeScript "typeable-openapi-diff-differ" ''
2125 #!${pkgs.stdenv.shell}
2226 set +e
23− ${typeable-comparest-exe} -c $1 -s $2 -o $3/typeable.md
27+ ${typeable-openapi-diff-exe} -c $1 -s $2 -o $3/typeable.md
2428 exit 0
2529 '';
2630
@@ -126,7 +130,7 @@ let
126130 })
127131 (getInputs root);
128132 differs = [
129− typeable-comparest-differ
133+ typeable-openapi-diff-differ
130134 atlassian-openapi-diff-differ
131135 openapitools-openapi-diff-differ
132136 oasdiff-differ
@@ -160,7 +164,7 @@ let
160164 tools = [
161165 "Typeable"
162166 "Atlassian"
163− "OpenAPI Tools"
167+ "OpenApi Tools"
164168 "oasdiff"
165169 "Bump.sh"
166170 ];
ci/policeman.nixdeleted−4
@@ -1,4 +0,0 @@
1−{ sources ? import ../nix/sources.nix
2−, pkgs ? import sources.nixpkgs { }
3−}:
4−pkgs.haskellPackages.policeman
css/awsm.min.cssdeleted−1
@@ -1 +0,0 @@
1−@charset "UTF-8";html{font-family:"PT Serif",Georgia,serif;font-size:100%;line-height:1.6;background:#fff;color:#000;-webkit-overflow-scrolling:touch}body{margin:1em;font-size:1rem}@media (min-width:20rem){body{font-size:calc(1rem + .0125*(100vw - 20rem))}}@media (min-width:40rem){body{font-size:1.25rem}}body article,body footer,body header,body main{position:relative;max-width:40rem;margin:0 auto}body>header{margin-bottom:3.5em}body>header h1{margin:0;font-size:1.5em}body>header p{margin:0;font-size:.85em}body>footer{margin-top:4.5em;padding-bottom:1.5em;text-align:center;font-size:.8rem;color:#aaa}details,nav,p{margin:1em 0}nav ul{list-style:none;margin:0;padding:0}nav li{display:inline-block;margin-right:1em;margin-bottom:.25em}nav a:visited{color:#0064c1}article header h1 a:visited:hover,article header h2 a:visited:hover,nav a:hover{color:#3000c1}ol,ul{margin-top:0;padding-top:0;padding-left:2.5em}p{-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}aside:first-child,p:first-child{margin-top:0}aside:last-child,p:last-child{margin-bottom:0}p+ol,p+ul{margin-top:-.75em}p img,p picture{float:right;margin-bottom:.5em;margin-left:.5em}p picture img{float:none;margin:0}blockquote,dd{padding-left:2.5em}dd{margin-bottom:1em;margin-left:0}dt{font-weight:700}blockquote{margin:0}aside{margin:.5em 0;font-style:italic;color:#aaa}@media (min-width:65rem){aside{position:absolute;right:-12.5rem;width:9.375rem;max-width:9.375rem;margin:0;padding-left:.5em;font-size:.8em;border-left:1px solid #f2f2f2}}section+section{margin-top:2em}h1,h2,h3,h4,h5,h6{margin:1.25em 0 0;line-height:1.2}h1:focus>a[href^="#"][id],h1:hover>a[href^="#"][id],h2:focus>a[href^="#"][id],h2:hover>a[href^="#"][id],h3:focus>a[href^="#"][id],h3:hover>a[href^="#"][id],h4:focus>a[href^="#"][id],h4:hover>a[href^="#"][id],h5:focus>a[href^="#"][id],h5:hover>a[href^="#"][id],h6:focus>a[href^="#"][id],h6:hover>a[href^="#"][id]{opacity:1;transition:opacity 0s,color 300ms ease-out}figure+p,h1+details,h1+p,h2+details,h2+p,h3+details,h3+p,h4+details,h4+p,h5+details,h5+p,h6+details,h6+p{margin-top:.5em}h1>a[href^="#"][id],h2>a[href^="#"][id],h3>a[href^="#"][id],h4>a[href^="#"][id],h5>a[href^="#"][id],h6>a[href^="#"][id]{position:absolute;left:-.5em;opacity:0;background:0 0;color:rgba(0,0,0,.5);transition:opacity 300ms ease-out}h1>a[href^="#"][id]:focus,h1>a[href^="#"][id]:hover,h1>a[href^="#"][id]:target,h2>a[href^="#"][id]:focus,h2>a[href^="#"][id]:hover,h2>a[href^="#"][id]:target,h3>a[href^="#"][id]:focus,h3>a[href^="#"][id]:hover,h3>a[href^="#"][id]:target,h4>a[href^="#"][id]:focus,h4>a[href^="#"][id]:hover,h4>a[href^="#"][id]:target,h5>a[href^="#"][id]:focus,h5>a[href^="#"][id]:hover,h5>a[href^="#"][id]:target,h6>a[href^="#"][id]:focus,h6>a[href^="#"][id]:hover,h6>a[href^="#"][id]:target{opacity:1;box-shadow:none;color:#000;transition:opacity 0s,color 0s}h1>a[href^="#"][id]:target:focus,h2>a[href^="#"][id]:target:focus,h3>a[href^="#"][id]:target:focus,h4>a[href^="#"][id]:target:focus,h5>a[href^="#"][id]:target:focus,h6>a[href^="#"][id]:target:focus{outline:0}h1>a[href^="#"][id]::before,h2>a[href^="#"][id]::before,h3>a[href^="#"][id]::before,h4>a[href^="#"][id]::before,h5>a[href^="#"][id]::before,h6>a[href^="#"][id]::before{content:"•"}h1{font-size:2em}h2{font-size:1.75em}h3{font-size:1.25em}h4{font-size:1.15em}h5,h6{font-size:1em}h6{margin-top:1em;color:#aaa}article+article{margin-top:5em}article header p{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:.6em;color:#aaa}article header p+h1,article header p+h2{margin-top:-.25em}article header h1+p,article header h2+p{margin-top:.25em}article header h1 a,article header h2 a{color:#000;background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3Crect x="0" y="0.5" width="1" height="0.5" fill="rgba(0, 0, 0, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){article header h1 a,article header h2 a{background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="2"%3E%3Crect x="0" y="1" width="1" height="1" fill="rgba(0, 0, 0, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}}article header h1 a:visited,article header h2 a:visited{color:#aaa;background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3Crect x="0" y="0.5" width="1" height="0.5" fill="rgba(170, 170, 170, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){article header h1 a:visited,article header h2 a:visited{background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="2"%3E%3Crect x="0" y="1" width="1" height="1" fill="rgba(170, 170, 170, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}}article>footer{margin-top:1.5em;font-size:.85em}a,abbr{text-decoration:none}a{color:#0064c1;transition:color 300ms ease-out,box-shadow 300ms ease-out;background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="1"%3E%3Crect x="0" y="0.5" width="1" height="0.5" fill="rgba(0, 100, 193, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){a{background:url('data:image/svg+xml;charset=utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="1" height="2"%3E%3Crect x="0" y="1" width="1" height="1" fill="rgba(0, 100, 193, 0.35)"/%3E%3C/svg%3E') left bottom repeat-x}}a:visited{color:#80b2e0}a:active,a:hover{outline-width:0}a:hover{box-shadow:inset 0 -1px #3000c1;color:#3000c1;transition:color 0s,box-shadow 0s}abbr{margin-right:-.075em;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none;letter-spacing:.075em;font-size:.9em;border-bottom:1px dotted}img,picture{display:block;max-width:100%;margin:0 auto}audio,video{width:100%;max-width:100%}video{-o-object-position:0 -1px;object-position:0 -1px}figure{margin:1em 0 .5em;padding:0}figure figcaption{opacity:.65;font-size:.85em}table{display:inline-block;border-spacing:0;border-collapse:collapse;overflow-x:auto;max-width:100%;text-align:left;vertical-align:top;background:linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0;background-attachment:scroll,scroll;background-size:1px 100%,1px 100%;background-repeat:no-repeat,no-repeat}table caption{font-size:.9em;background:#fff}table td,table th{padding:.35em .75em;vertical-align:top;font-size:.9em;border:1px solid #f2f2f2;border-top:0;border-left:0}table td:first-child,table th:first-child{padding-left:0;background-image:linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%);background-size:2px 100%;background-repeat:no-repeat}table td:last-child,table th:last-child{padding-right:0;border-right:0;background-image:linear-gradient(to left,#fff 50%,rgba(255,255,255,0) 100%);background-position:100% 0;background-size:2px 100%;background-repeat:no-repeat}table td:only-child,table th:only-child{background-image:linear-gradient(to right,#fff 50%,rgba(255,255,255,0) 100%),linear-gradient(to left,#fff 50%,rgba(255,255,255,0) 100%);background-position:0 0,100% 0;background-size:2px 100%,2px 100%;background-repeat:no-repeat,no-repeat}table th{line-height:1.2}code,kbd,samp,var{font-family:Consolas,"Lucida Console",Monaco,monospace;font-style:normal}pre{overflow-x:auto;font-size:.8em;background:linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 0 0,linear-gradient(rgba(0,0,0,.15) 0%,rgba(0,0,0,.15) 100%) 100% 0;background-attachment:scroll,scroll;background-size:1px 100%,1px 100%;background-repeat:no-repeat,no-repeat}pre>code,summary{display:inline-block}pre>code{overflow-x:visible;box-sizing:border-box;min-width:100%;border-right:3px solid #fff;border-left:1px solid #fff}hr{height:1px;margin:2em 0;border:0;background:#f2f2f2}details[open]{padding-bottom:.5em;border-bottom:1px solid #f2f2f2}summary{font-weight:700;border-bottom:1px dashed;cursor:pointer}summary::-webkit-details-marker{display:none}noscript{color:#d00000}::selection{background:rgba(0,100,193,.15)}
default.nixdeleted−125
@@ -1,125 +0,0 @@
1−{ sources ? import ./nix/sources.nix
2−, haskellNix ? import sources.haskellNix { inherit system; }
3−, pkgs ? import haskellNix.sources.nixpkgs-2311 (haskellNix.nixpkgsArgs // { inherit system; })
4−, system ? builtins.currentSystem
5−, nix-filter ? import sources.nix-filter
6−}:
7−let
8− masterPkgs = import sources.nixpkgs { inherit system; };
9− yq = import ./nix/yq.nix { pkgs = masterPkgs; };
10− diffYaml = import ./nix/diff-yaml.nix { pkgs = masterPkgs; };
11−
12− hsPkgs = pkgs.haskell-nix.stackProject {
13− src = nix-filter {
14− root = ./.;
15− name = "compaREST";
16− include = [
17− ./stack.yaml
18− ./stack.yaml.lock
19− ./compaREST.cabal
20− ];
21− };
22−
23− modules = [
24− {
25− dontStrip = false;
26− dontPatchELF = false;
27− enableDeadCodeElimination = true;
28− ghcOptions = [
29− "-O2"
30− "-fexpose-all-unfoldings"
31− "-fspecialise-aggressively"
32− ];
33− packages.pandoc.ghcOptions = [ "-O1" ];
34− packages.compaREST.src = nix-filter {
35− root = ./.;
36− name = "compaREST-src";
37− include = with nix-filter; [
38− (./compaREST.cabal)
39− (inDirectory ./test)
40− (inDirectory ./src)
41− (inDirectory ./app)
42− (inDirectory ./github-action)
43− ./css/awsm.min.css
44− ./LICENSE
45− ];
46− };
47− }
48− ];
49− };
50−
51− staticify = name: drv: pkgs.runCommand name { } ''
52− mkdir -p $out/bin
53− cp ${drv + "/bin"}/* $out/bin
54−
55− ${pkgs.nukeReferences}/bin/nuke-refs $out/bin/*
56− '';
57−
58− compaRESTBin = hsPkgs.compaREST.components.exes.compaREST;
59− compaRESTStaticBin = (staticify "compaREST-static" hsPkgs.projectCross.musl64.hsPkgs.compaREST.components.exes.compaREST);
60−
61−
62− # doesn't work
63− armDarwinCompaREST = hsPkgs.projectCross.aarch64-darwin.hsPkgs.compaREST.components.exes.compaREST;
64−
65− compaRESTImage = pkgs.dockerTools.buildImage {
66− name = "compaREST";
67− contents = [ compaRESTStaticBin ];
68− config = {
69− Entrypoint = [ "/bin/compaREST" ];
70− };
71− };
72−
73− macOSCompaRESTBundle = pkgs.runCommand "compaREST-macOS-bundled"
74− {
75− buildInputs = [ masterPkgs.macdylibbundler ];
76− } ''
77− mkdir -p $out/lib
78− cp ${compaRESTBin}/bin/compaREST $out/compaREST
79− chmod 755 $out/compaREST
80− dylibbundler -b \
81− -x $out/compaREST \
82− -d $out/lib \
83− -p '@executable_path/lib'
84− '';
85−
86−
87− compaRESTGithubAction =
88− let
89− action = staticify "compaREST-GitHub-Action-static" hsPkgs.projectCross.musl64.hsPkgs.compaREST.components.exes.compaREST-GitHub-Action;
90− wrapped = pkgs.runCommand "wrapped-compaREST-GitHub-Action" { buildInputs = [ pkgs.makeWrapper ]; } ''
91− makeWrapper ${action}/bin/compaREST-GitHub-Action $out/bin/pre --add-flags "pre"
92− makeWrapper ${action}/bin/compaREST-GitHub-Action $out/bin/run --add-flags "run"
93− '';
94− in
95− pkgs.dockerTools.buildImage {
96− name = "typeable/compaREST-GitHub-Action";
97− tag = "latest";
98− contents = [ wrapped pkgs.cacert ];
99− };
100−
101− WindowsCompaRESTBin = hsPkgs.projectCross.mingwW64.hsPkgs.compaREST.components.exes.compaREST;
102−
103− # We use the static version so that we don't have to rebuild everything on CI.
104− # The only binaries build on CI are static.
105− test = hsPkgs.projectCross.musl64.hsPkgs.compaREST.components.tests.compaREST-tests.overrideAttrs (final: old: {
106− buildInputs = old.buildInputs ++ [ diffYaml yq ];
107− nativeBuildInputs = old.nativeBuildInputs ++ (with masterPkgs; [ makeWrapper ]);
108−
109− postInstall = ''
110− wrapProgram $out/bin/compaREST-tests \
111− --prefix PATH : ${masterPkgs.lib.makeBinPath [ diffYaml yq ]}
112− '';
113− });
114−in
115−{
116− inherit
117− compaRESTImage
118− compaRESTGithubAction
119− compaRESTStaticBin
120− compaRESTBin
121− hsPkgs
122− macOSCompaRESTBundle
123− WindowsCompaRESTBin
124− test;
125−}
docs/Development_guide.mddeleted−149
@@ -1,149 +0,0 @@
1−CompaREST
2−=========
3−The tool works by simultaneously looking at a pair of nodes in OpenAPI schema
4−trees. At every place we try to understand whether one side (the producer) can
5−produce something that the other side (the consumer) cannot consume. This often
6−involves recursively running the same check on child sub-nodes. The necessary
7−criteria are implemented manually for all types of nodes that appear in the
8−schema. The `Subtree` class in `Data.OpenApi.Compare.Subtree` designates types
9−of nodes for which this compatibility checking is defined. The implementations
10−for various types reside in `Data.OpenApi.Compare.Validate.*`.
11−
12−Producer/Consumer
13−-----------------
14−Rather than looking at which schema is the server's and which is the client's,
15−in terms of compatibility checking it is more useful to track which side is the
16−"producer" and which is the "consumer". At the root of the schema the producer
17−is the client, but as we descend into an HTTP response, the direction flips and
18−the producer becomes the server.
19−
20−We deal with a lot of things in pairs, where one thing belongs to the producer
21−and another belongs to the consumer. The `ProdCons` datatype (in
22−`Data.OpenApi.Compare.Subtree`) is just a pair type that provides an Applicative
23−abstraction for working with two things simultaneously.
24−
25−Trees
26−-----
27−We directly use the datatypes defined in the `openapi3` library. For reasons of
28−identifiability and memoization we tag all nodes in the trees with a path from
29−the root. The tree is heterogeneous (nodes have different types), and every type
30−of node has a different arrangement of children. There is a `Step` data family
31−that defines all the possible children (of a specific type) of a node (of a
32−specific type).
33−
34−A `Trace` is a sequence of steps, except in a path the types of adjacent steps
35−have to agree. The `Paths` datatype achieves this. Together all paths form a
36−`Category`, and we often concatenate them using `>>>`. This is defined in
37−`Data.OpenApi.Compare.Paths`.
38−
39−We often keep a node and a path to it next to each other using an environment
40−comonad. `Traced` is a type alias around `Env` which ensures that the type of
41−the path matches the type of the node.
42−
43−Issues
44−------
45−The output of the tool is a list of compatibility issues that came up during
46−checking. The `Issue` data family describes the kinds of issues that can occur
47−at each type of node. An issue is supposed to characterize a set of interactions
48−(requests or responses) that demonstrate that the schemas are incompatible.
49−
50−Each issue is tagged with a `Behavior` that describes the path to reproducing
51−the issue. While a `Trace` describes a syntactic path down the schema tree, a
52−`Behavior` describes which part of an interaction needs to be focused on for the
53−issue to manifest itself.
54−
55−In most places where we keep multiple issues (e.g. the output), to store them we
56−use a prefix tree map, indexed by both types and values of `Behavior`s. See
57−`Data.OpenApi.Compare.PathsPrefixTree`.
58−
59−Report
60−------
61−The checker ultimately outputs a list of `Issue`s, together with `Behavior`s
62−that cause them. We run the checker twice: forwards and backwards -- to detect
63−non-breaking changes. All of this is then compiled into a report in
64−`Data.OpenApi.Compare.Report`. The tree structure of headings is computed from
65−`Behavior`s, using `Jet`s to collapse particular sequences of behavior steps
66−into a single human-readable header item. The text of each paragraph comes from
67−`describeIssue`.
68−
69−We then use the `pandoc` library to render the report in a variety of formats.
70−
71−Formulas
72−--------
73−The schema has references, so it can end up being recursive. Similarly the
74−process for establishing compatibility can end up being recursive. To keep track
75−of this, the checker actually manipulates *formulas* which can have variables in
76−them, defined in (`Data.OpenApi.Compare.Formula`).
77−
78−A formula represents a set of issues, with the empty set corresponding to a
79−successful compatibility check. A formula can have conjunctions, where we only
80−report success if all parts succeeded, and if not we take the union of all
81−issues. A formula can also have disjunctions, but there it's impossible to
82−guarantee that the issues for the parts also make sense as issues for the whole
83−thing. Due to this if all disjuncted parts have issues we instead return a
84−different issue (usually non-descriptive).
85−
86−The `FormulaF` datatype implements a functor that carries formulaic calculations
87−as well as a result value (a co-Yoneda extension of formulas for the most part).
88−The Applicative interface provides conjunction, and the Alternative interface
89−provides disjunction.
90−
91−When we detect recursion we introduce a variable, compute the compatibility
92−check as a formula with that variable, and then we solve a fixed point equation
93−(`maxFixpoint`) with the variable to obtain the answer.
94−
95−This `FomulaF` is further wrapped in a couple monad transformers
96−(`SemanticCompatFormula`, `StructuralCompatFormula`), and the whole checker is
97−then implemented in the resulting Applicative using ApplicativeDo.
98−
99−Memoization
100−-----------
101−The `Data.OpenApi.Compare.Memo` module contains utilities for stateful
102−memoization and recursion detection. The entire checking process is memoized,
103−and can detect, propagate, and resolve recursion.
104−
105−Structural/Semantic Compatibility
106−---------------------------------
107−When we encounter two nodes we first try to optimistically establish whether
108−they're structurally equal, i.e. the trees are exactly the same modulo reference
109−inlining. This check is also memoized and recursion-aware. If the check succeeds
110−we conclude that the schemas are compatible. Otherwise we fall back to the
111−"normal" semantic compatibility.
112−
113−
114−JSON Schema
115−-----------
116−The language that describes JSON payloads is probably the richest part of
117−OpenAPI schemas, and the checker for it also comes with a lot of moving parts.
118−
119−JSON schema allows arbitrary set arithmetic using `allOf`, `anyOf`, `not` and
120−`oneOf`. To fully check `not` and `oneOf` we would need to compare set
121−subtraction, which involves negating a comparison result. If recursion is
122−involved we would need to be able to solve equations with negation, which we
123−cannot do. So `not` is unsupported altogether, and `oneOf` is only supported
124−when it has disjoint branches and behaves like `anyOf`.
125−
126−With that in mind a JSON schema is first pre-processed into a Disjunctive Normal
127−Form (`DNF` in `Data.OpenApi.Compare.Validate.Schema.DNF`). This pre-processing
128−happens in `Data.OpenApi.Compare.Validate.Schema.Process`.
129−
130−The DNF contains elementary clauses that match on a specific aspect of a JSON
131−object (`Condition`). Most clauses only affect objects of a single type, so JSON
132−values and clauses are segregated by type (`JsonType`, `TypedValue` in
133−`Data.OpenApi.Compare.Validate.Schema.JsonFormula`)
134−
135−Then we reduce the problem of comparing two DNF's to the comparison of a
136−conjunction of clauses in the producer with a single clause in the consumer
137−(`checkFormulas` in `Data.OpenApi.Compare.Validate.Schema`).
138−
139−When we do disjunction in the formula we lose information, so we try to avoid
140−having disjunctions in the consumer DNF. This is done by the means of
141−partitioning. We look for factors that would let us partition the set of all
142−objects into several bins, in a way that hopefully corresponds only a single
143−consumer to each producer. This is implemented in
144−`Data.OpenApi.Compare.Validate.Schema.Partition`. Currently we can only
145−partition by an `enum` value that is accessible via a chain of `required`
146−fields.
147−
148−The same partitioning mechanism is used to test whether the branches of a
149−`oneOf` are disjoint, and emit a warning otherwise.
docs/Integration_guide.mddeleted−49
@@ -1,49 +0,0 @@
1−# Integration guide
2−
3−## Github Actions
4−
5−In cases where your OpenAPI spec can be generated from your code, it can be useful to see how a pull request changes your API. For this very purpose we have a Github Action.
6−
7−### Generating the inputs
8−
9−To use it you need to first get the old (before the pull request) and new (after the pull request) specs.
10−
11−For example:
12−
13−- To get the old spec you can:
14− - Download from a public "production" server (if your api is public and you provide an OpenAPI spec)
15− - Checkout the base commit of the pull request and generate is programmatically.
16−
17− You can checkout the base commit like so in Github Actions:
18−
19− ```bash
20− git checkout $(git merge-base ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.base.sha }})
21− ```
22−- To get the new spec you can:
23− - Build and run the spec generation for the head commit of the pull request
24−
25−### Run the action
26−
27−In our example we have already generated the two OpenAPI specs and placed them in the `old-openapi.yaml` and `new-openapi.yaml` files.
28−
29−We can now run our action:
30−
31−```yaml
32−- uses: typeable/comparest
33− with:
34− old: old-openapi.yaml
35− new: new-openapi.yaml
36− project_name: TEST
37−```
38−
39−This will create a check on the pull request displaying the changes (if there are any) similar to this:
40−
41−![](img/github-action-report.png)
42−
43−The check will show success when there are no breaking changes, and be neutral otherwise.
44−
45−## Integrating into something other than Github Actions
46−
47−We have a dedicated implementation only for Github Actions. However, we provide a generic CLI which can be used to integrate compaREST into arbitrary workflows and CI systems.
48−
49−You see an example of CLI usage in our [readme](../README.md#assessing-compatibility-automatically).
docs/Releasing.mddeleted−15
@@ -1,15 +0,0 @@
1−# The release procedure
2−
3−1. Update the version number everywhere:
4− 1. Version in [compaREST.cabal](../compaREST.cabal)
5− 2. Docker tag in [action.yaml](../action.yaml)
6− 3. Set the version of unreleased changes in [CHANGELOG.md](../CHANGELOG.md)
7−2. Publish!
8− 1. Create a pre-release on GitHub:
9− ![](img/pre-release.png)
10− 2. This will automatically start the release procedure. You can check the state of it in the "Actions" tab on GitHub:
11− ![](img/pre-release-ci.png)
12− 3. If everything goes as planned, the release action should pass and all assets should be added to the release:
13− ![](img/pre-release-artifacts.png)
14− 4. The new version should be automatically uploaded a release candidate to [Hackage](https://hackage.haskell.org/package/compaREST/candidates/). Make sure everything looks okay and publish it.
15− 5. Go to the Releases tab on GitHub and edit the release to make it not a "pre-release".
docs/User_guide.mddeleted−51
@@ -1,51 +0,0 @@
1−CompaREST User Guide
2−====================
3−
4−Running the Tool
5−----------------
6−The tool accepts two OpenAPI 3.0.0 schema files in either JSON or YAML format.
7−One is assumed to be the client version of the schema, and the other is the
8−server version.
9−
10−The tool will look for changes between the schemas and detect whether they are
11−breaking or not -- that is, if they prevent interoperability between the client
12−and the server.
13−
14−Running:
15−```
16−comparest -c client.json -s server.json
17−```
18−will output a markdown report of the changes to the standard output.
19−
20−Compatibility status can be signaled via the exit code using
21−`--signal-exit-code`. In case no report is needed, output can be suppressed with
22−`--silent`. For example:
23−```
24−comparest -c client.json -s server.json --signal-exit-code --silent
25−echo $?
26−```
27−If there were changes that the tool determined to be breaking, the exit code
28−will be 1. If there were some changes the tool couldn't understand, the exit
29−code will be 2. Otherwise if there were no breaking changes, the exit code will
30−be 0.
31−
32−Controlling the Report
33−----------------------
34−By default the report includes breaking changes, as well as non-breaking:
35−changes that would be considered breaking in the opposite direction. To only
36−include breaking changes in the report use `--only-breaking`. The `--all` option
37−restores the default behavior.
38−
39−The report can be formatted in a variety of ways supported by Pandoc. The `-o`
40−option causes the report to be written to a file. The format of the file is
41−determined from the extension. The supported extensions include:
42− - `.md` for markdown
43− - `.html` for an HTML snippet
44− - `.rst` for restructured text
45− - no extension for a self-contained HTML document with styles
46−
47−By default the report is split up into parts relating to different paths,
48−requests, responses, etc, using headers of various levels. Alternatively, the
49−report can use indended block-quotes to visualize the tree structure of the
50−report. The header style is enabled with `--header-style`, and the block-quote
51−style is enabled with `--folding-block-quotes-style`.
docs/img/github-action-report.pngdeleted

Binary file not shown.

docs/img/pre-release-artifacts.pngdeleted

Binary file not shown.

docs/img/pre-release-ci.pngdeleted

Binary file not shown.

docs/img/pre-release.pngdeleted

Binary file not shown.

fourmolu.yamldeleted−8
@@ -1,8 +0,0 @@
1−indentation: 2
2−comma-style: leading # for lists, tuples etc. - can also be 'trailing'
3−record-brace-space: true # rec {x = 1} vs. rec{x = 1}
4−indent-wheres: true # 'false' means save space by only half-indenting the 'where' keyword
5−diff-friendly-import-export: false # 'false' uses Ormolu-style lists
6−respectful: true # don't be too opinionated about newlines etc.
7−haddock-style: single-line # '--' vs. '{-'
8−newlines-between-decls: 1 # number of newlines between top-level declarations
github-action/CompaREST/GitHub/API.hsdeleted−86
@@ -1,86 +0,0 @@
1−module CompaREST.GitHub.API
2− ( postStatus,
3− postStatusProcessing,
4− )
5−where
6−
7−import CompaREST.GitHub.Action.Config
8−import Control.Monad.Freer
9−import Control.Monad.Freer.GitHub
10−import Control.Monad.Freer.Reader
11−import Control.Monad.IO.Class
12−import Data.Aeson
13−import qualified Data.ByteString.Lazy.Char8 as BSLC
14−import Data.OpenApi.Compare.Report
15−import Data.Proxy
16−import Data.Text (Text)
17−import GitHub
18−import GitHub.Data.Checks
19−import GitHub.Endpoints.Checks
20−
21−postStatusProcessing ::
22− (Members '[GitHub, Reader Config] effs, MonadIO (Eff effs)) =>
23− Eff effs ()
24−postStatusProcessing = do
25− Config {..} <- ask
26− printJSON $
27− sendGitHub $
28− checkR
29− repoOwner
30− repoName
31− Check
32− { checkName = mkName Proxy checkName
33− , checkSha = sha
34− , checkDetailsURL = Nothing
35− , checkExternalId = Nothing
36− , checkStatus = Just CheckInProgress
37− , checkStartedAt = Nothing
38− , checkConclusion = Nothing
39− , checkCompletedAt = Nothing
40− , checkOutput = Nothing
41− , checkActions = Nothing
42− }
43−
44−postStatus ::
45− (Members '[GitHub, Reader Config] effs, MonadIO (Eff effs)) =>
46− -- | 'Nothing' means that there were no changes at all
47− Maybe (Text, ReportStatus) ->
48− Eff effs ()
49−postStatus x = do
50− let (body, (title, conclusion)) = case x of
51− Just (b, s) -> (b,) $ case s of
52− BreakingChanges -> ("⚠️ Breaking changes found!", CheckNeutral)
53− NoBreakingChanges -> ("No breaking changes found ✨", CheckSuccess)
54− OnlyUnsupportedChanges -> ("🤷 Couldn't determine compatibility", CheckNeutral)
55− Nothing -> ("", ("✅ The API did not change", CheckSuccess))
56− Config {..} <- ask
57− printJSON $
58− sendGitHub $
59− checkR
60− repoOwner
61− repoName
62− Check
63− { checkName = mkName Proxy checkName
64− , checkSha = sha
65− , checkDetailsURL = Nothing
66− , checkExternalId = Nothing
67− , checkStatus = Just CheckCompleted
68− , checkStartedAt = Nothing
69− , checkConclusion = Just conclusion
70− , checkCompletedAt = Nothing
71− , checkOutput =
72− Just $
73− CheckOutput
74− { checkTitle = title
75− , checkSummary = body
76− , checkText = Nothing
77− , checkAnnotations = Nothing
78− , checkImages = Nothing
79− }
80− , checkActions = Nothing
81− }
82−
83−printJSON :: MonadIO (Eff effs) => Eff effs Value -> Eff effs ()
84−printJSON m = do
85− x <- m
86− liftIO . BSLC.putStrLn $ encode x
github-action/CompaREST/GitHub/Action/Config.hsdeleted−47
@@ -1,47 +0,0 @@
1−module CompaREST.GitHub.Action.Config
2− ( Config (..),
3− )
4−where
5−
6−import Data.Proxy
7−import Data.Text (Text)
8−import qualified Data.Text as T
9−import qualified GitHub as GH
10−
11−import Data.Functor
12−import System.Envy
13−
14−data Config = Config
15− { githubToken :: GH.Auth
16− , repoOwner :: GH.Name GH.Owner
17− , repoName :: GH.Name GH.Repo
18− , checkName :: Text
19− , footerText :: Text
20− , root :: FilePath
21− , sha :: GH.Name GH.Commit
22− }
23−
24−instance FromEnv Config where
25− fromEnv _ = do
26− token <- GH.OAuth <$> env "GITHUB_TOKEN"
27− (owner, repo) <-
28− T.split (== '/') <$> env "REPO" >>= \case
29− [owner, name] -> pure (owner, name)
30− _ -> fail "malformed repo"
31− checkName <-
32− envMaybe "PROJECT_NAME" <&> \case
33− Just pName | not . T.null . T.strip $ pName -> "compaREST – " <> pName
34− _ -> "compaREST"
35− footerText <- env "FOOTER"
36− root <- envMaybe "ROOT" .!= "."
37− sha <- env "SHA"
38− pure $
39− Config
40− { githubToken = token
41− , repoOwner = GH.mkName Proxy owner
42− , repoName = GH.mkName Proxy repo
43− , checkName = checkName
44− , footerText = footerText
45− , root = root
46− , sha = GH.mkName Proxy sha
47− }
github-action/Control/Monad/Freer/GitHub.hsdeleted−27
@@ -1,27 +0,0 @@
1−module Control.Monad.Freer.GitHub
2− ( GitHub (..),
3− runGitHub,
4− sendGitHub,
5− )
6−where
7−
8−import Control.Monad.Freer
9−import Control.Monad.Freer.Error
10−import Control.Monad.IO.Class
11−import Data.Aeson
12−import GitHub hiding (Error)
13−import qualified GitHub as GH
14−
15−data GitHub r where
16− SendGHRequest :: FromJSON x => Request 'RW x -> GitHub x
17−
18−runGitHub :: (Member (Error GH.Error) effs, MonadIO (Eff effs)) => Auth -> Eff (GitHub ': effs) ~> Eff effs
19−runGitHub auth =
20− interpret
21− ( \(SendGHRequest req) ->
22− liftIO (executeRequest auth req)
23− >>= either throwError pure
24− )
25−
26−sendGitHub :: (FromJSON x, Member GitHub effs) => Request 'RW x -> Eff effs x
27−sendGitHub req = send $ SendGHRequest req
github-action/GitHub/Data/Checks.hsdeleted−173
@@ -1,173 +0,0 @@
1−module GitHub.Data.Checks
2− ( Check (..),
3− CheckStatus (..),
4− CheckConclusion (..),
5− CheckOutput (..),
6− CheckAnnotation (..),
7− CheckAnnotationLevel (..),
8− CheckImage (..),
9− CheckAction (..),
10− )
11−where
12−
13−import Data.Aeson
14−import Data.Aeson.Types
15−import Data.Text (Text)
16−import Data.Vector (Vector)
17−import GHC.Generics (Generic)
18−import GitHub
19−import GitHub.Internal.Prelude (UTCTime)
20−
21−data Check = Check
22− { checkName :: !(Name Check)
23− , checkSha :: !(Name Commit)
24− , checkDetailsURL :: !(Maybe URL)
25− , checkExternalId :: !(Maybe (Id Check))
26− , checkStatus :: !(Maybe CheckStatus)
27− , checkStartedAt :: !(Maybe UTCTime)
28− , checkConclusion :: !(Maybe CheckConclusion)
29− , checkCompletedAt :: !(Maybe UTCTime)
30− , checkOutput :: !(Maybe CheckOutput)
31− , checkActions :: !(Maybe (Vector CheckAction))
32− }
33− deriving stock (Show, Eq, Ord, Generic)
34−
35−instance ToJSON Check where
36− toJSON (Check n sha durl eid s sa c ca o a) =
37− object'
38− [ "name" .= n
39− , "head_sha" .= sha
40− , "details_url" .= durl
41− , "external_id" .= eid
42− , "status" .= s
43− , "started_at" .= sa
44− , "conclusion" .= c
45− , "completed_at" .= ca
46− , "output" .= o
47− , "actions" .= a
48− ]
49−
50−data CheckStatus
51− = CheckQueued
52− | CheckInProgress
53− | CheckCompleted
54− deriving stock (Show, Enum, Bounded, Eq, Ord, Generic)
55−
56−instance ToJSON CheckStatus where
57− toJSON CheckQueued = String "queued"
58− toJSON CheckInProgress = String "in_progress"
59− toJSON CheckCompleted = String "completed"
60−
61−data CheckConclusion
62− = CheckActionRequired
63− | CheckCancelled
64− | CheckFailure
65− | CheckNeutral
66− | CheckSuccess
67− | CheckSkipped
68− | CheckStale
69− | CheckTimedOut
70− deriving stock (Show, Enum, Bounded, Eq, Ord, Generic)
71−
72−instance ToJSON CheckConclusion where
73− toJSON CheckActionRequired = String "action_required"
74− toJSON CheckCancelled = String "cancelled"
75− toJSON CheckFailure = String "failure"
76− toJSON CheckNeutral = String "neutral"
77− toJSON CheckSuccess = String "success"
78− toJSON CheckSkipped = String "skipped"
79− toJSON CheckStale = String "stale"
80− toJSON CheckTimedOut = String "timed_out"
81−
82−data CheckOutput = CheckOutput
83− { checkTitle :: !Text
84− , checkSummary :: !Text
85− , checkText :: !(Maybe Text)
86− , checkAnnotations :: !(Maybe (Vector CheckAnnotation))
87− , checkImages :: !(Maybe (Vector CheckImage))
88− }
89− deriving stock (Show, Eq, Ord, Generic)
90−
91−instance ToJSON CheckOutput where
92− toJSON (CheckOutput t s txt a i) =
93− object'
94− [ "title" .= t
95− , "summary" .= s
96− , "text" .= txt
97− , "annotations" .= a
98− , "images" .= i
99− ]
100−
101−data CheckAnnotation = CheckAnnotation
102− { checkPath :: !Text
103− , checkStartLine :: !Int
104− , checkEndLine :: !Int
105− , checkStartColumn :: !(Maybe Int)
106− , checkEndColumn :: !(Maybe Int)
107− , checkAnnotationLevel :: !CheckAnnotationLevel
108− , checkMessage :: !Text
109− , checkTitle :: !(Maybe Text)
110− , checkRawDetails :: !(Maybe Text)
111− }
112− deriving stock (Show, Eq, Ord, Generic)
113−
114−instance ToJSON CheckAnnotation where
115− toJSON (CheckAnnotation p sl el sc ec al m t rd) =
116− object'
117− [ "path" .= p
118− , "start_line" .= sl
119− , "end_line" .= el
120− , "start_column" .= sc
121− , "end_column" .= ec
122− , "annotation_level" .= al
123− , "message" .= m
124− , "title" .= t
125− , "raw_details" .= rd
126− ]
127−
128−data CheckAnnotationLevel
129− = NoticeAnnotation
130− | WarningAnnotation
131− | FailureAnnotation
132− deriving stock (Show, Enum, Bounded, Eq, Ord, Generic)
133−
134−instance ToJSON CheckAnnotationLevel where
135− toJSON NoticeAnnotation = String "notice"
136− toJSON WarningAnnotation = String "warning"
137− toJSON FailureAnnotation = String "failure"
138−
139−data CheckImage = CheckImage
140− { checkImageAlt :: !Text
141− , checkImageURL :: !URL
142− , checkImageCaption :: !(Maybe Text)
143− }
144− deriving stock (Show, Eq, Ord, Generic)
145−
146−instance ToJSON CheckImage where
147− toJSON (CheckImage a url c) =
148− object'
149− [ "alt" .= a
150− , "image_url" .= url
151− , "caption" .= c
152− ]
153−
154−data CheckAction = CheckAction
155− { checkActionLabel :: !Text
156− , checkActionDescription :: !Text
157− , checkActionIdentifier :: !Text
158− }
159− deriving stock (Show, Eq, Ord, Generic)
160−
161−instance ToJSON CheckAction where
162− toJSON (CheckAction l d i) =
163− object'
164− [ "label" .= l
165− , "description" .= d
166− , "identifier" .= i
167− ]
168−
169−object' :: [Pair] -> Value
170−object' = object . filter notNull
171− where
172− notNull (_, Null) = False
173− notNull (_, _) = True
github-action/GitHub/Endpoints/Checks.hsdeleted−12
@@ -1,12 +0,0 @@
1−module GitHub.Endpoints.Checks
2− ( checkR,
3− )
4−where
5−
6−import Data.Aeson
7−import GitHub
8−import GitHub.Data.Checks
9−
10−checkR :: Name Owner -> Name Repo -> Check -> Request 'RW Value
11−checkR user repo =
12− command Post ["repos", toPathPart user, toPathPart repo, "check-runs"] . encode
github-action/Main.hsdeleted−72
@@ -1,72 +0,0 @@
1−module Main (main) where
2−
3−import CompaREST.GitHub.API
4−import CompaREST.GitHub.Action.Config
5−import Control.Exception
6−import Control.Monad.Freer
7−import Control.Monad.Freer.Error
8−import Control.Monad.Freer.GitHub
9−import Control.Monad.Freer.Reader
10−import Data.OpenApi.Compare.Run
11−import Data.Text (Text)
12−import qualified Data.Text as T
13−import qualified Data.Yaml.Aeson as Yaml
14−import qualified GitHub as GH
15−import System.Environment
16−import System.Envy (decodeEnv)
17−import System.FilePath ((</>))
18−import Text.Pandoc (runPure)
19−import Text.Pandoc.Builder
20−import Text.Pandoc.Options
21−import Text.Pandoc.Writers
22−
23−main :: IO ()
24−main = do
25− cfg <- decodeEnv >>= either error pure
26− case sha cfg of
27− x | T.null . T.strip . GH.untagName $ x -> do
28− putStrLn "SHA not specified. Exiting without doing anything."
29− pure ()
30− _ ->
31− getArgs >>= \case
32− ["pre"] -> runPre cfg
33− ["run"] -> do
34− oldFile <- getEnv "OLD"
35− newFile <- getEnv "NEW"
36− runRun cfg (root cfg </> oldFile) (root cfg </> newFile)
37− _ -> error "Invalid arguments."
38−
39−runner :: Config -> Eff '[GitHub, Error GH.Error, Reader Config, IO] a -> IO a
40−runner cfg =
41− runM @IO . runReader cfg
42− . flip (handleError @GH.Error) (error . displayException)
43− . runGitHub (githubToken cfg)
44−
45−runPre :: Config -> IO ()
46−runPre cfg = runner cfg postStatusProcessing
47−
48−runRun :: Config -> FilePath -> FilePath -> IO ()
49−runRun cfg old' new' = runner cfg $ do
50− old <- Yaml.decodeFileThrow old'
51− new <- Yaml.decodeFileThrow new'
52− let reportConfig =
53− ReportConfig
54− { treeStyle = FoldingBlockquotesTreeStyle
55− , reportMode = All
56− }
57− (report, status) = runReport reportConfig (old, new)
58−
59− body = markdown report <> "\n\n" <> footerText cfg
60−
61− result =
62− if old == new
63− then Nothing
64− else Just (body, status)
65− postStatus result
66−
67−markdown :: Blocks -> Text
68−markdown =
69− either (error . displayException) id
70− . runPure
71− . writeHtml5String def
72− . doc
nix/ci.nixdeleted−11
@@ -1,11 +0,0 @@
1−{ sources ? import ./sources.nix
2−, pkgs ? import sources.nixpkgs { }
3−}:
4−
5−let
6− yq = import ./yq.nix { inherit pkgs; };
7− diff-yaml = import ./diff-yaml.nix { inherit pkgs; };
8−in
9−pkgs.mkShell {
10− packages = [ pkgs.haskellPackages.fourmolu diff-yaml yq ];
11−}
nix/diff-yaml.nixdeleted−20
@@ -1,20 +0,0 @@
1−{ pkgs ? import <nixpkgs> {} }:
2−
3−let
4− inherit (pkgs) stdenv;
5− yq = import ./yq.nix { inherit pkgs; };
6−in
7−stdenv.mkDerivation {
8− name = "diff-yaml";
9−
10− src = ../scripts/diff-yaml;
11−
12− buildInputs = [ pkgs.bash yq ];
13−
14− phases = [ "installPhase" ];
15−
16− installPhase = ''
17− mkdir -p $out/bin
18− cp $src $out/bin/diff-yaml
19− '';
20−}
nix/shell.nixdeleted−11
@@ -1,11 +0,0 @@
1−{ sources ? import ./sources.nix
2−, pkgs ? import sources.nixpkgs { }
3−}:
4−
5−let
6− patched-yq = import ./yq.nix { inherit pkgs; };
7− diffYaml = import ./diff-yaml.nix { inherit pkgs; };
8−in
9−pkgs.mkShell {
10− buildInputs = with pkgs; [ pkg-config diffYaml patched-yq zlib ];
11−}
nix/sources.jsondeleted−50
@@ -1,50 +0,0 @@
1−{
2− "haskellNix": {
3− "branch": "master",
4− "description": "Alternative Haskell Infrastructure for Nixpkgs",
5− "homepage": "https://input-output-hk.github.io/haskell.nix",
6− "owner": "input-output-hk",
7− "repo": "haskell.nix",
8− "rev": "d7f95903c84a5a50d6fe0844280da82c3c81680a",
9− "sha256": "02870rk4sx44lwwa0k20iwdhqcx0ayz34yb3b0qy045p22q45815",
10− "type": "tarball",
11− "url": "https://github.com/input-output-hk/haskell.nix/archive/d7f95903c84a5a50d6fe0844280da82c3c81680a.tar.gz",
12− "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
13− },
14− "niv": {
15− "branch": "master",
16− "description": "Easy dependency management for Nix projects",
17− "homepage": "https://github.com/nmattia/niv",
18− "owner": "nmattia",
19− "repo": "niv",
20− "rev": "f7c538837892dd2eb83567c9f380a11efb59b53f",
21− "sha256": "0xl33k24vfc29cg9lnp95kvcq69qbq5fzb7jk9ig4lgrhaarh651",
22− "type": "tarball",
23− "url": "https://github.com/nmattia/niv/archive/f7c538837892dd2eb83567c9f380a11efb59b53f.tar.gz",
24− "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
25− },
26− "nix-filter": {
27− "branch": "main",
28− "description": "a small self-container source filtering lib",
29− "homepage": "",
30− "owner": "numtide",
31− "repo": "nix-filter",
32− "rev": "3342559a24e85fc164b295c3444e8a139924675b",
33− "sha256": "08xia32g5jzaiyhfl3kzxslzbr4w1i56i0mhy5gpfggv8gqy8sym",
34− "type": "tarball",
35− "url": "https://github.com/numtide/nix-filter/archive/3342559a24e85fc164b295c3444e8a139924675b.tar.gz",
36− "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
37− },
38− "nixpkgs": {
39− "branch": "master",
40− "description": "Nix Packages collection",
41− "homepage": "",
42− "owner": "NixOS",
43− "repo": "nixpkgs",
44− "rev": "bf038bd724982de67a2ee9a6b2f1fcb7d6a87bd7",
45− "sha256": "0ivvwdmg9fipj6ylq5ipxqpb0xnz6hsx3lyn377jacfpiqi9w0cv",
46− "type": "tarball",
47− "url": "https://github.com/NixOS/nixpkgs/archive/bf038bd724982de67a2ee9a6b2f1fcb7d6a87bd7.tar.gz",
48− "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
49− }
50−}
nix/sources.nixdeleted−174
@@ -1,174 +0,0 @@
1−# This file has been generated by Niv.
2−
3−let
4−
5− #
6− # The fetchers. fetch_<type> fetches specs of type <type>.
7− #
8−
9− fetch_file = pkgs: name: spec:
10− let
11− name' = sanitizeName name + "-src";
12− in
13− if spec.builtin or true then
14− builtins_fetchurl { inherit (spec) url sha256; name = name'; }
15− else
16− pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
17−
18− fetch_tarball = pkgs: name: spec:
19− let
20− name' = sanitizeName name + "-src";
21− in
22− if spec.builtin or true then
23− builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
24− else
25− pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
26−
27− fetch_git = name: spec:
28− let
29− ref =
30− if spec ? ref then spec.ref else
31− if spec ? branch then "refs/heads/${spec.branch}" else
32− if spec ? tag then "refs/tags/${spec.tag}" else
33− abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
34− in
35− builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; };
36−
37− fetch_local = spec: spec.path;
38−
39− fetch_builtin-tarball = name: throw
40− ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
41− $ niv modify ${name} -a type=tarball -a builtin=true'';
42−
43− fetch_builtin-url = name: throw
44− ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
45− $ niv modify ${name} -a type=file -a builtin=true'';
46−
47− #
48− # Various helpers
49− #
50−
51− # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
52− sanitizeName = name:
53− (
54− concatMapStrings (s: if builtins.isList s then "-" else s)
55− (
56− builtins.split "[^[:alnum:]+._?=-]+"
57− ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
58− )
59− );
60−
61− # The set of packages used when specs are fetched using non-builtins.
62− mkPkgs = sources: system:
63− let
64− sourcesNixpkgs =
65− import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
66− hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
67− hasThisAsNixpkgsPath = <nixpkgs> == ./.;
68− in
69− if builtins.hasAttr "nixpkgs" sources
70− then sourcesNixpkgs
71− else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
72− import <nixpkgs> {}
73− else
74− abort
75− ''
76− Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
77− add a package called "nixpkgs" to your sources.json.
78− '';
79−
80− # The actual fetching function.
81− fetch = pkgs: name: spec:
82−
83− if ! builtins.hasAttr "type" spec then
84− abort "ERROR: niv spec ${name} does not have a 'type' attribute"
85− else if spec.type == "file" then fetch_file pkgs name spec
86− else if spec.type == "tarball" then fetch_tarball pkgs name spec
87− else if spec.type == "git" then fetch_git name spec
88− else if spec.type == "local" then fetch_local spec
89− else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
90− else if spec.type == "builtin-url" then fetch_builtin-url name
91− else
92− abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
93−
94− # If the environment variable NIV_OVERRIDE_${name} is set, then use
95− # the path directly as opposed to the fetched source.
96− replace = name: drv:
97− let
98− saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
99− ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
100− in
101− if ersatz == "" then drv else
102− # this turns the string into an actual Nix path (for both absolute and
103− # relative paths)
104− if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
105−
106− # Ports of functions for older nix versions
107−
108− # a Nix version of mapAttrs if the built-in doesn't exist
109− mapAttrs = builtins.mapAttrs or (
110− f: set: with builtins;
111− listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
112− );
113−
114− # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
115− range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);
116−
117− # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
118− stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
119−
120− # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
121− stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
122− concatMapStrings = f: list: concatStrings (map f list);
123− concatStrings = builtins.concatStringsSep "";
124−
125− # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
126− optionalAttrs = cond: as: if cond then as else {};
127−
128− # fetchTarball version that is compatible between all the versions of Nix
129− builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
130− let
131− inherit (builtins) lessThan nixVersion fetchTarball;
132− in
133− if lessThan nixVersion "1.12" then
134− fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
135− else
136− fetchTarball attrs;
137−
138− # fetchurl version that is compatible between all the versions of Nix
139− builtins_fetchurl = { url, name ? null, sha256 }@attrs:
140− let
141− inherit (builtins) lessThan nixVersion fetchurl;
142− in
143− if lessThan nixVersion "1.12" then
144− fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
145− else
146− fetchurl attrs;
147−
148− # Create the final "sources" from the config
149− mkSources = config:
150− mapAttrs (
151− name: spec:
152− if builtins.hasAttr "outPath" spec
153− then abort
154− "The values in sources.json should not have an 'outPath' attribute"
155− else
156− spec // { outPath = replace name (fetch config.pkgs name spec); }
157− ) config.sources;
158−
159− # The "config" used by the fetchers
160− mkConfig =
161− { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
162− , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
163− , system ? builtins.currentSystem
164− , pkgs ? mkPkgs sources system
165− }: rec {
166− # The sources, i.e. the attribute set of spec name to spec
167− inherit sources;
168−
169− # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
170− inherit pkgs;
171− };
172−
173−in
174−mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }
nix/yq.nixdeleted−26
@@ -1,26 +0,0 @@
1−{ pkgs ? import <nixpkgs> {} }:
2−
3−# Make yq work inside whatever `stack test` launches.
4−# Nuke this when https://github.com/kislyuk/yq/pull/193 is merged
5−# and nixpkgs includes that fix.
6−
7−let
8− yq-patch = pkgs.writeText "yq-tty-fix.patch" ''
9−diff --git a/yq/__init__.py b/yq/__init__.py
10−index d95ac4b..ae7d59e 100644
11−--- a/yq/__init__.py
12−+++ b/yq/__init__.py
13−@@ -110,7 +110,7 @@ def cli(args=None, input_format="yaml", program_name="yq"):
14− in_place = args.in_place
15− delattr(args, "in_place")
16−
17−- if sys.stdin.isatty() and not args.input_streams:
18−+ if (sys.stdin is None or sys.stdin.isatty()) and not args.input_streams:
19− parser.print_help()
20− sys.exit(2)
21− elif not args.input_streams:
22− '';
23−in
24−pkgs.yq.overrideAttrs (final: old: {
25− patches = old.patches ++ [ yq-patch ];
26−})
compaREST.cabal → openapi-diff.cabalrenamed+60−89
@@ -1,24 +1,29 @@
11cabal-version: 2.4
2−name: compaREST
3−version: 0.1.0.1
2+name: openapi-diff
3+version: 0.0.0.0
44synopsis: Compatibility checker for OpenAPI
5−description: Compatibility checker for OpenAPI.
5+description: Compatibility checker for OpenAPI
66license: MIT
77license-file: LICENSE
8−author: Typeable
9−maintainer: compaREST@typeable.io
10−copyright: 2021 Typeable
8+author: Aleksey Uymanov
9+maintainer: Aleksey Uymanov <s9gf4ult@gmail.com>
10+copyright: 2021 Aleksey Uymanov
1111category: Web
1212build-type: Simple
1313extra-doc-files: README.md
1414 CHANGELOG.md
15−tested-with: GHC == 9.6
16−extra-source-files: css/awsm.min.css
17−extra-source-files:
18− test/golden/**/*.yaml
19− test/golden/**/*.md
15+tested-with: GHC == 8.10.4
16+extra-source-files: awsm-css/dist/awsm.min.css
17+
18+Flag static
19+ Default: False
20+ Manual: True
2021
2122common common-options
23+ if flag(static)
24+ cc-options: -static
25+ ld-options: -static -pthread
26+ ghc-options: -static
2227 ghc-options:
2328 -Weverything
2429 -Wno-implicit-prelude
@@ -26,17 +31,16 @@ common common-options
2631 -Wno-safe
2732 -Wno-prepositive-qualified-module
2833 -Wno-missing-import-lists
29− -Wno-missing-kind-signatures
3034 -Wno-partial-fields
3135 -Wno-all-missed-specialisations
3236 -Wno-missing-local-signatures
3337 -Wno-unsafe
3438 -fconstraint-solver-iterations=0
3539 default-language: Haskell2010
36− build-depends: base >= 4.12.0.0 && < 5
40+ build-depends: base >= 4.12.0.0 && < 4.16
41+ , bytestring
3742 , text
3843
39−
4044 default-extensions: ApplicativeDo
4145 , BangPatterns
4246 , ConstraintKinds
@@ -78,7 +82,6 @@ common common-options
7882 , QuantifiedConstraints
7983 , DefaultSignatures
8084
81−
8285library
8386 import: common-options
8487 build-depends:
@@ -108,53 +111,52 @@ library
108111 , file-embed
109112 , data-default
110113 , ordered-containers
111− , bytestring
112114 hs-source-dirs: src
113− exposed-modules: Data.OpenApi.Compare.Behavior
114− , Data.OpenApi.Compare.Common
115− , Data.OpenApi.Compare.Formula
116− , Data.OpenApi.Compare.Memo
117− , Data.OpenApi.Compare.Orphans
118− , Data.OpenApi.Compare.Paths
119− , Data.OpenApi.Compare.PathsPrefixTree
120− , Data.OpenApi.Compare.References
121− , Data.OpenApi.Compare.Report
122− , Data.OpenApi.Compare.Report.Html.Template
123− , Data.OpenApi.Compare.Report.Jet
124− , Data.OpenApi.Compare.Run
125− , Data.OpenApi.Compare.Subtree
126− , Data.OpenApi.Compare.Validate.Header
127− , Data.OpenApi.Compare.Validate.Link
128− , Data.OpenApi.Compare.Validate.MediaTypeObject
129− , Data.OpenApi.Compare.Validate.OAuth2Flows
130− , Data.OpenApi.Compare.Validate.OpenApi
131− , Data.OpenApi.Compare.Validate.Operation
132− , Data.OpenApi.Compare.Validate.Param
133− , Data.OpenApi.Compare.Validate.PathFragment
134− , Data.OpenApi.Compare.Validate.Products
135− , Data.OpenApi.Compare.Validate.RequestBody
136− , Data.OpenApi.Compare.Validate.Responses
137− , Data.OpenApi.Compare.Validate.Schema
138− , Data.OpenApi.Compare.Validate.Schema.DNF
139− , Data.OpenApi.Compare.Validate.Schema.Issues
140− , Data.OpenApi.Compare.Validate.Schema.JsonFormula
141− , Data.OpenApi.Compare.Validate.Schema.Partition
142− , Data.OpenApi.Compare.Validate.Schema.Process
143− , Data.OpenApi.Compare.Validate.Schema.Traced
144− , Data.OpenApi.Compare.Validate.Schema.TypedJson
145− , Data.OpenApi.Compare.Validate.SecurityRequirement
146− , Data.OpenApi.Compare.Validate.SecurityScheme
147− , Data.OpenApi.Compare.Validate.Server
148− , Data.OpenApi.Compare.Validate.Sums
149− other-modules: Data.HList
115+ exposed-modules: Data.HList
116+ , OpenAPI.Checker.Behavior
117+ , OpenAPI.Checker.Formula
118+ , OpenAPI.Checker.Memo
119+ , OpenAPI.Checker.Orphans
120+ , OpenAPI.Checker.Paths
121+ , OpenAPI.Checker.PathsPrefixTree
122+ , OpenAPI.Checker.References
123+ , OpenAPI.Checker.Subtree
124+ , OpenAPI.Checker.Validate.MediaTypeObject
125+ , OpenAPI.Checker.Validate.OpenApi
126+ , OpenAPI.Checker.Validate.Operation
127+ , OpenAPI.Checker.Validate.Param
128+ , OpenAPI.Checker.Validate.PathFragment
129+ , OpenAPI.Checker.Validate.Products
130+ , OpenAPI.Checker.Validate.RequestBody
131+ , OpenAPI.Checker.Validate.Responses
132+ , OpenAPI.Checker.Validate.Schema
133+ , OpenAPI.Checker.Validate.Schema.TypedJson
134+ , OpenAPI.Checker.Validate.Schema.DNF
135+ , OpenAPI.Checker.Validate.Schema.Issues
136+ , OpenAPI.Checker.Validate.Schema.JsonFormula
137+ , OpenAPI.Checker.Validate.Schema.Partition
138+ , OpenAPI.Checker.Validate.Schema.Process
139+ , OpenAPI.Checker.Validate.Schema.Traced
140+ , OpenAPI.Checker.Validate.SecurityRequirement
141+ , OpenAPI.Checker.Validate.Server
142+ , OpenAPI.Checker.Validate.Sums
143+ , OpenAPI.Checker.Validate.Header
144+ , OpenAPI.Checker.Validate.Link
145+ , OpenAPI.Checker.Common
146+ , OpenAPI.Checker.Validate.SecurityScheme
147+ , OpenAPI.Checker.Validate.OAuth2Flows
148+ , OpenAPI.Checker.Report
150149 , Data.OpenUnion.Extra
150+ , OpenAPI.Checker.Report.Jet
151+ , OpenAPI.Checker.Run
152+ , OpenAPI.Checker.Report.Html.Template
151153
152−executable compaREST
154+executable openapi-diff
153155 import: common-options
154156 hs-source-dirs: app
155157 main-is: Main.hs
156158 build-depends:
157− compaREST
159+ openapi-diff
158160 , pandoc
159161 , data-default
160162 , bytestring
@@ -165,42 +167,14 @@ executable compaREST
165167 , aeson
166168 , containers
167169 , doctemplates
168− , pandoc-types
169− , prettyprinter-compat-ansi-wl-pprint
170170 ghc-options: -threaded
171171 -rtsopts
172172 -with-rtsopts=-N
173173 other-modules:
174174 FormatHeuristic
175− Data.OpenApi.Compare.Options
176−
177−executable compaREST-GitHub-Action
178− import: common-options
179− hs-source-dirs: github-action
180− main-is: Main.hs
181− build-depends:
182− compaREST
183− , pandoc
184− , yaml
185− , aeson
186− , github
187− , freer-simple
188− , vector
189− , pandoc-types
190− , envy
191− , filepath
192− , bytestring
193− ghc-options: -threaded
194− -rtsopts
195− -with-rtsopts=-N
196− other-modules:
197− Control.Monad.Freer.GitHub
198− CompaREST.GitHub.API
199− CompaREST.GitHub.Action.Config
200− GitHub.Data.Checks
201− GitHub.Endpoints.Checks
175+ OpenAPI.Checker.Options
202176
203−test-suite compaREST-tests
177+test-suite openapi-diff-test
204178 import: common-options
205179 type: exitcode-stdio-1.0
206180 hs-source-dirs: test
@@ -208,10 +182,8 @@ test-suite compaREST-tests
208182 other-modules:
209183 Spec.Golden.Extra
210184 Spec.Golden.TraceTree
211− Paths_compaREST
212− autogen-modules: Paths_compaREST
213185 build-depends:
214− , compaREST
186+ , openapi-diff
215187 , tasty-golden
216188 , tasty
217189 , bytestring
@@ -221,7 +193,6 @@ test-suite compaREST-tests
221193 , pandoc
222194 , data-default
223195 , lens
224− , pandoc-types
225196 ghc-options: -threaded
226197 -rtsopts
227198 -with-rtsopts=-N
scripts/diff-yamldeleted−35
@@ -1,35 +0,0 @@
1−#!/usr/bin/env bash
2−set -e
3−
4−# Normalise YAML files passed as inputs, then run diff on them.
5−
6−check_util() {
7− local util=$1
8−
9− if [ -z $util ]; then
10− echo "Error: $util not found in PATH"
11− exit 1
12− fi
13−}
14−
15−DIFF=`which diff`
16−check_util $DIFF
17−
18−YQ=`which yq`
19−check_util $YQ
20−
21−# Only normalise YAML files.
22−# Print anything else as is.
23−normalise() {
24− local file=$1
25− local contents=($YQ -y -S '.' $file)
26−
27− # if $contents contains actual YAML data, print it
28− if [[ $? == 0 ]]; then
29− echo $contents
30− else
31− cat $file
32− fi
33−}
34−
35−$DIFF -u <(normalise $1) <(normalise $2)
src/Data/HList.hs+11−11
@@ -1,11 +1,11 @@
11module Data.HList
2− ( Has,
3− HasAll,
4− getH,
5− HList (..),
6− singletonH,
7− ReassembleHList,
8− reassemble,
2+ ( Has
3+ , HasAll
4+ , getH
5+ , HList (..)
6+ , singletonH
7+ , ReassembleHList
8+ , reassemble
99 )
1010where
1111
@@ -40,8 +40,8 @@ instance (Has' x xs t, HeadEq x (y : xs) ~ 'False) => Has' x (y ': xs) 'False wh
4040 {-# INLINE getH #-}
4141
4242instance
43− TypeError ( 'ShowType x ':<>: 'Text " is not a part of the list.") =>
44− Has' x '[] 'False
43+ TypeError ('ShowType x ':<>: 'Text " is not a part of the list.")
44+ => Has' x '[] 'False
4545 where
4646 getH HNil = undefined
4747 {-# INLINE getH #-}
@@ -74,8 +74,8 @@ instance ReassembleHList' (x ': xs) '[] 'False where
7474 {-# INLINE reassemble #-}
7575
7676instance
77− (Has y xs, ReassembleHList' xs ys f, (xs == (y ': ys)) ~ 'False) =>
78− ReassembleHList' xs (y ': ys) 'False
77+ (Has y xs, ReassembleHList' xs ys f, (xs == (y ': ys)) ~ 'False)
78+ => ReassembleHList' xs (y ': ys) 'False
7979 where
8080 reassemble xs = getH @y xs `HCons` reassemble xs
8181 {-# INLINE reassemble #-}
src/Data/OpenApi/Compare/Memo.hsdeleted−164
@@ -1,164 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−
3−-- | Utilities for effectfully memoizing other, more effectful functions.
4−module Data.OpenApi.Compare.Memo
5− ( MonadMemo,
6− MemoState,
7− runMemo,
8− modifyMemoNonce,
9− KnotTier (..),
10− unknot,
11− memoWithKnot,
12− memoTaggedWithKnot,
13− )
14−where
15−
16−import Control.Monad.State
17−import Data.Dynamic
18−import qualified Data.Map as M
19−import Data.Tagged
20−import qualified Data.TypeRepMap as T
21−import Data.Void
22−import Type.Reflection
23−
24−data Progress a = Finished a | Started | TyingKnot Dynamic
25−
26−data MemoMap a where
27− MemoMap :: !(M.Map k (Progress v)) -> MemoMap (k, v)
28−
29−data MemoState s = MemoState s (T.TypeRepMap MemoMap)
30−
31−-- | An effectful memoization monad.
32−type MonadMemo s m = MonadState (MemoState s) m
33−
34−memoStateLookup ::
35− forall k v s.
36− (Typeable k, Typeable v, Ord k) =>
37− k ->
38− MemoState s ->
39− Maybe (Progress v)
40−memoStateLookup k (MemoState _ t) = case T.lookup @(k, v) t of
41− Just (MemoMap m) -> M.lookup k m
42− Nothing -> Nothing
43−
44−memoStateInsert ::
45− forall k v s.
46− (Typeable k, Typeable v, Ord k) =>
47− k ->
48− Progress v ->
49− MemoState s ->
50− MemoState s
51−memoStateInsert k x (MemoState s t) = MemoState s $ T.insert (MemoMap m'') t
52− where
53− m'' = M.insert k x m'
54− m' = case T.lookup @(k, v) t of
55− Just (MemoMap m) -> m
56− Nothing -> M.empty
57−
58−modifyMemoNonce :: MonadMemo s m => (s -> s) -> m s
59−modifyMemoNonce f = do
60− MemoState s t <- get
61− put $ MemoState (f s) t
62− pure s
63−
64−-- | Run a memoized computation.
65−runMemo :: Monad m => s -> StateT (MemoState s) m a -> m a
66−runMemo s = (`evalStateT` MemoState s T.empty)
67−
68−-- | A description of how to effectfully tie knots in type @v@, using the @m@
69−-- monad, and by sharing some @d@ data among the recursive instances.
70−data KnotTier v d m = KnotTier
71− { -- | Create some data that will be connected to this knot
72− onKnotFound :: m d
73− , -- | This is what the knot will look like as a value
74− -- to the inner computations
75− onKnotUsed :: d -> m v
76− , -- | Once we're done and we're outside, tie the
77− -- knot using the datum
78− tieKnot :: d -> v -> m v
79− }
80−
81−unknot :: KnotTier v Void m
82−unknot =
83− KnotTier
84− { onKnotFound = error "Recursion detected"
85− , onKnotUsed = absurd
86− , tieKnot = absurd
87− }
88−
89−-- | Run a potentially recursive computation. The provided key will be used to
90−-- refer to the result of this computation. If during the computation, another
91−-- attempt to run the computation with the same key is made, we run a
92−-- tying-the-knot procedure.
93−--
94−-- If another attempt to run the computation with the same key is made
95−-- *after we're done*, we will return the memoized value.
96−memoWithKnot ::
97− forall k v d m s.
98− (Typeable k, Typeable v, Typeable d, Ord k, MonadMemo s m) =>
99− KnotTier v d m ->
100− -- | the computation to memoize
101− m v ->
102− -- | key for memoization
103− k ->
104− m v
105−memoWithKnot tier f k =
106− memoStateLookup @k @v k <$> get >>= \case
107− Just (Finished v) -> pure v
108− Just Started -> do
109− d <- onKnotFound tier
110− modify $ memoStateInsert @k @v k (TyingKnot $ toDyn d)
111− onKnotUsed tier d
112− Just (TyingKnot dyn) -> case fromDynamic dyn of
113− Just d -> onKnotUsed tier d
114− Nothing ->
115− error $
116− "Type mismatch when examining the knot of "
117− <> show (typeRep @(k -> v))
118− <> ": expected "
119− <> show (typeRep @d)
120− <> ", got "
121− <> show (dynTypeRep dyn)
122− Nothing -> do
123− modify $ memoStateInsert @k @v k Started
124− v <- f
125− v' <-
126− memoStateLookup @k @v k <$> get >>= \case
127− Just Started -> pure v
128− Just (TyingKnot dyn) -> case fromDynamic dyn of
129− Just d -> tieKnot tier d v
130− Nothing ->
131− error $
132− "Type mismatch when tying the knot of "
133− <> show (typeRep @(k -> v))
134− <> ": expected "
135− <> show (typeRep @d)
136− <> ", got "
137− <> show (dynTypeRep dyn)
138− Just (Finished _) ->
139− error $
140− "Unexpected Finished when memoizing "
141− <> show (typeRep @(k -> v))
142− Nothing -> pure v
143− -- Normally this would be an error, but the underlying monad can refuse
144− -- to remember memoization state
145− modify $ memoStateInsert @k @v k (Finished v')
146− pure v'
147−
148−-- | Disambiguate memoized computations with an arbitrary tag.
149−memoTaggedWithKnot ::
150− forall t k v d m s.
151− ( Typeable t
152− , Typeable k
153− , Typeable v
154− , Typeable d
155− , Ord k
156− , MonadMemo s m
157− ) =>
158− KnotTier v d m ->
159− m v ->
160− k ->
161− m v
162−memoTaggedWithKnot tier f k =
163− withTypeable (typeRepKind $ typeRep @t) $
164− memoWithKnot tier f (Tagged @t k)
src/Data/OpenApi/Compare/Run.hsdeleted−31
@@ -1,31 +0,0 @@
1−module Data.OpenApi.Compare.Run
2− ( runChecker,
3− runReport,
4− module Data.OpenApi.Compare.Report,
5− )
6−where
7−
8−import Data.HList
9−import Data.OpenApi (OpenApi)
10−import Data.OpenApi.Compare.Paths
11−import Data.OpenApi.Compare.Report
12−import Data.OpenApi.Compare.Subtree
13−import Data.OpenApi.Compare.Validate.OpenApi ()
14−import Text.Pandoc.Builder
15−
16−runChecker :: (OpenApi, OpenApi) -> CheckerOutput
17−runChecker (client, server) =
18− CheckerOutput
19− { forwardChanges = run client server
20− , backwardChanges = run server client
21− }
22− where
23− toPC p c =
24− ProdCons
25− { producer = traced (step ClientSchema) p
26− , consumer = traced (step ServerSchema) c
27− }
28− run p c = either id mempty . runCompatFormula . checkCompatibility Root HNil $ toPC p c
29−
30−runReport :: ReportConfig -> (OpenApi, OpenApi) -> (Blocks, ReportStatus)
31−runReport cfg = generateReport cfg . segregateIssues . runChecker
src/Data/OpenApi/Compare/Validate/Products.hsdeleted−48
@@ -1,48 +0,0 @@
1−-- | Checks product-like entities. The key is some identificator for the product
2−--element. Each element may be required or optional.
3−--
4−--One example of product is request parameters. There are optional and required
5−--parameters. The client and server have possibly different set of
6−--parameters. What we must check is if server requires some request parameter,
7−--then this parameter must be presented by client and their schemas must match.
8−--
9−--So when we checking products we are checking from the server's (consumer)
10−--perspective, ensuring that all parameters are provided by the client (producer)
11−--and their schemas match.
12−--
13−--This module abstracts this logic for arbitrary elements
14−module Data.OpenApi.Compare.Validate.Products
15− ( checkProducts,
16− ProductLike (..),
17− )
18−where
19−
20−import Data.Foldable
21−import Data.Map.Strict (Map)
22−import qualified Data.Map.Strict as M
23−import Data.OpenApi.Compare.Behavior
24−import Data.OpenApi.Compare.Paths
25−import Data.OpenApi.Compare.Subtree
26−
27−-- | Some entity which is product-like
28−data ProductLike a = ProductLike
29− { productValue :: a
30− , required :: Bool
31− }
32−
33−checkProducts ::
34− (Ord k, Issuable l) =>
35− Paths q r l ->
36− -- | No required element found
37− (k -> Issue l) ->
38− (k -> ProdCons t -> CompatFormula' q AnIssue r ()) ->
39− ProdCons (Map k (ProductLike t)) ->
40− CompatFormula' q AnIssue r ()
41−checkProducts xs noElt check (ProdCons p c) = for_ (M.toList c) $ \(key, consElt) ->
42− case M.lookup key p of
43− Nothing -> case required consElt of
44− True -> issueAt xs $ noElt key
45− False -> pure ()
46− Just prodElt -> do
47− let elts = ProdCons prodElt consElt
48− check key (productValue <$> elts)
src/Data/OpenApi/Compare/Validate/Sums.hsdeleted−25
@@ -1,25 +0,0 @@
1−module Data.OpenApi.Compare.Validate.Sums
2− ( checkSums,
3− )
4−where
5−
6−import Data.Foldable
7−import Data.Map.Strict (Map)
8−import qualified Data.Map.Strict as M
9−import Data.OpenApi.Compare.Behavior
10−import Data.OpenApi.Compare.Paths
11−import Data.OpenApi.Compare.Subtree
12−
13−checkSums ::
14− (Ord k, Issuable l) =>
15− Paths q r l ->
16− (k -> Issue l) ->
17− (k -> ProdCons t -> CompatFormula' q AnIssue r ()) ->
18− ProdCons (Map k t) ->
19− CompatFormula' q AnIssue r ()
20−checkSums xs noElt check (ProdCons p c) = for_ (M.toList p) $ \(key, prodElt) ->
21− case M.lookup key c of
22− Nothing -> issueAt xs $ noElt key
23− Just consElt ->
24− let sumElts = ProdCons prodElt consElt
25− in check key sumElts
src/Data/OpenUnion/Extra.hs+5−5
@@ -1,7 +1,7 @@
11module Data.OpenUnion.Extra
2− ( (@@>),
3− TryLiftUnion (..),
4− pattern SingletonUnion,
2+ ( (@@>)
3+ , TryLiftUnion (..)
4+ , pattern SingletonUnion
55 )
66where
77
@@ -18,8 +18,8 @@ instance TryLiftUnion '[] where
1818 tryLiftUnion _ = empty
1919
2020instance
21− (Typeable y, SubList ys (y : ys), TryLiftUnion ys) =>
22− TryLiftUnion (y ': ys)
21+ (Typeable y, SubList ys (y : ys), TryLiftUnion ys)
22+ => TryLiftUnion (y ': ys)
2323 where
2424 tryLiftUnion (x :: x) = case eqT @x @y of
2525 Nothing -> reUnion <$> tryLiftUnion @ys x
src/Data/OpenApi/Compare/Behavior.hs → src/OpenAPI/Checker/Behavior.hsrenamed+11−49
@@ -1,22 +1,18 @@
1−module Data.OpenApi.Compare.Behavior
2− ( BehaviorLevel (..),
3− Behavable (..),
4− IssueKind (..),
5− Issuable (..),
6− Orientation (..),
7− toggleOrientation,
8− Behavior,
9− AnIssue (..),
10− withClass,
11− anIssueKind,
12− relatedAnIssues,
1+module OpenAPI.Checker.Behavior
2+ ( BehaviorLevel (..)
3+ , Behavable (..)
4+ , Issuable (..)
5+ , Orientation (..)
6+ , toggleOrientation
7+ , Behavior
8+ , AnIssue (..)
139 )
1410where
1511
1612import Data.Aeson
1713import Data.Kind
18−import Data.OpenApi.Compare.Paths
1914import Data.Typeable
15+import OpenAPI.Checker.Paths
2016import Text.Pandoc.Builder
2117
2218-- | Kind
@@ -43,23 +39,10 @@ class
4339 Behavable (a :: BehaviorLevel) (b :: BehaviorLevel)
4440 where
4541 data Behave a b
46− describeBehavior :: Behave a b -> Inlines
42+ describeBehaviour :: Behave a b -> Inlines
4743
4844type instance AdditionalQuiverConstraints Behave a b = Behavable a b
4945
50−data IssueKind
51− = -- | This is certainly an issue, we can demonstrate a "counterexample"
52− CertainIssue
53− | -- | Change looks breaking but we don't have a complete comprehension of the problem
54− ProbablyIssue
55− | -- | We don't really support this feature at all, outside structural comparison
56− Unsupported
57− | -- | This is not an issue in itself, but a clarifying comment providing context for some other issues
58− Comment
59− | -- | We detected an issue with one of the input schemata itself
60− SchemaInvalid
61− deriving stock (Eq, Ord, Show)
62−
6346class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLevel) where
6447 data Issue l :: Type
6548
@@ -75,22 +58,7 @@ class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLeve
7558 -- If _producer_ doesn't have something, the element was "added".
7659 describeIssue :: Orientation -> Issue l -> Blocks
7760
78− issueKind :: Issue l -> IssueKind
79−
80− -- | An equivalence relation designating whether two issues are talking about the aspect of the schema. This is used
81− -- to remove duplicates from the "reverse" error tree we get when we look for non-breaking changes.
82− -- Generally if checking X->Y raises issue I, and checking Y->X raises issue J, I and J should be related.
83− relatedIssues :: Issue l -> Issue l -> Bool
84− relatedIssues = (==)
85−
86−-- Utility function for 'relatedIssues'. In @withClass eq f@, @f@ attempts to partition inputs into equivalence classes,
87−-- and two items in the same equivalence class are related. If both items aren't assigned to a class by @f@, instead
88−-- @eq@ is used to compare them.
89−withClass :: Eq b => (a -> a -> Bool) -> (a -> Maybe b) -> a -> a -> Bool
90−withClass eq f = \x y -> case (f x, f y) of
91− (Just fx, Just fy) -> fx == fy
92− (Nothing, Nothing) -> eq x y
93− (_, _) -> False
61+ issueIsUnsupported :: Issue l -> Bool
9462
9563data Orientation = Forward | Backward
9664 deriving stock (Eq, Ord, Show)
@@ -116,9 +84,3 @@ deriving stock instance Ord (AnIssue l)
11684
11785instance ToJSON (AnIssue l) where
11886 toJSON (AnIssue _ issue) = toJSON issue
119−
120−anIssueKind :: AnIssue l -> IssueKind
121−anIssueKind (AnIssue _ i) = issueKind i
122−
123−relatedAnIssues :: AnIssue l -> AnIssue l -> Bool
124−relatedAnIssues (AnIssue _ x) (AnIssue _ y) = relatedIssues x y
src/Data/OpenApi/Compare/Common.hs → src/OpenAPI/Checker/Common.hsrenamed+2−2
@@ -1,5 +1,5 @@
1−module Data.OpenApi.Compare.Common
2− ( zipAll,
1+module OpenAPI.Checker.Common
2+ ( zipAll
33 )
44where
55
src/Data/OpenApi/Compare/Formula.hs → src/OpenAPI/Checker/Formula.hsrenamed+25−35
@@ -1,21 +1,20 @@
1−module Data.OpenApi.Compare.Formula
2− ( FormulaF,
3− VarRef,
4− variable,
5− eitherOf,
6− anError,
7− errors,
8− calculate,
9− maxFixpoint,
10− mapErrors,
11− )
12−where
1+module OpenAPI.Checker.Formula
2+ ( FormulaF
3+ , VarRef
4+ , variable
5+ , eitherOf
6+ , anError
7+ , errors
8+ , calculate
9+ , maxFixpoint
10+ , mapErrors
11+ ) where
1312
1413import Data.Kind
15−import qualified Data.List.NonEmpty as NE
1614import Data.Monoid
17−import Data.OpenApi.Compare.Paths
18−import qualified Data.OpenApi.Compare.PathsPrefixTree as P
15+import qualified Data.List.NonEmpty as NE
16+import OpenAPI.Checker.Paths
17+import qualified OpenAPI.Checker.PathsPrefixTree as P
1918
2019type VarRef = Int
2120
@@ -25,24 +24,15 @@ type VarRef = Int
2524-- fixpoints always exist, i.e. that @x = f x@ has at least one solution.
2625data FormulaF (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: Type) where
2726 Result :: a -> FormulaF q f r a
28− Errors ::
29− !(P.PathsPrefixTree q f r) ->
30− -- | invariant: never empty
31− FormulaF q f r a
32− Apply ::
33− FormulaF q f r (b -> c) ->
34− FormulaF q f r b ->
35− (c -> a) ->
36− -- | invariant: at least one of LHS and RHS is not 'Errors', and they are
27+ Errors :: !(P.PathsPrefixTree q f r) -> FormulaF q f r a
28+ -- ^ invariant: never empty
29+ Apply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
30+ -- ^ invariant: at least one of LHS and RHS is not 'Errors', and they are
3731 -- both not 'Result'
38− FormulaF q f r a
39− SelectFirst ::
40− NE.NonEmpty (SomeFormulaF b) ->
41− !(P.PathsPrefixTree q f r) ->
42− (b -> a) ->
43− -- | invariant: the list doesn't contain any 'Result's, 'Errors' or
32+ SelectFirst :: NE.NonEmpty (SomeFormulaF b)
33+ -> !(P.PathsPrefixTree q f r) -> (b -> a) -> FormulaF q f r a
34+ -- ^ invariant: the list doesn't contain any 'Result's, 'Errors' or
4435 -- 'SelectFirst'
45− FormulaF q f r a
4636 Variable :: !VarRef -> a -> FormulaF q f r a
4737
4838mkApply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
@@ -54,13 +44,13 @@ mkApply f x h = Apply f x h
5444mkSelectFirst :: [SomeFormulaF b] -> P.PathsPrefixTree q f r -> (b -> a) -> FormulaF q f r a
5545mkSelectFirst fs allE h = case foldMap check fs of
5646 (First (Just x), _) -> Result (h x)
57− (First Nothing, x : xs) -> SelectFirst (x NE.:| xs) allE h
47+ (First Nothing, x:xs) -> SelectFirst (x NE.:| xs) allE h
5848 (First Nothing, []) -> Errors allE
5949 where
6050 check (SomeFormulaF (Result x)) = (First (Just x), mempty)
6151 check (SomeFormulaF (Errors _)) = (mempty, mempty)
62− check (SomeFormulaF (SelectFirst xs _ h')) =
63− (mempty, NE.toList (fmap (fmap h') xs))
52+ check (SomeFormulaF (SelectFirst xs _ h'))
53+ = (mempty, NE.toList (fmap (fmap h') xs))
6454 check x = (mempty, [x])
6555
6656data SomeFormulaF (a :: Type) where
@@ -106,7 +96,7 @@ calculate (Apply f x h) = case calculate f of
10696 Right x' -> Right (h (f' x'))
10797calculate (SelectFirst xs e h) = go (NE.toList xs)
10898 where
109− go (SomeFormulaF r : rs) = case calculate r of
99+ go (SomeFormulaF r:rs) = case calculate r of
110100 Left _ -> go rs
111101 Right x -> Right (h x)
112102 go [] = Left e
src/OpenAPI/Checker/Memo.hsnew file+126
@@ -0,0 +1,126 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+-- | Utilities for effectfully memoizing other, more effectful functions.
3+module OpenAPI.Checker.Memo
4+ ( MonadMemo
5+ , MemoState
6+ , runMemo
7+ , modifyMemoNonce
8+ , KnotTier(..)
9+ , unknot
10+ , memoWithKnot
11+ , memoTaggedWithKnot
12+ ) where
13+
14+import Control.Monad.State
15+import Data.Dynamic
16+import qualified Data.Map as M
17+import Data.Tagged
18+import Data.Void
19+import qualified Data.TypeRepMap as T
20+import Type.Reflection
21+
22+data Progress a = Finished a | Started | TyingKnot Dynamic
23+
24+data MemoMap a where
25+ MemoMap :: !(M.Map k (Progress v)) -> MemoMap (k, v)
26+
27+data MemoState s = MemoState s (T.TypeRepMap MemoMap)
28+
29+-- | An effectful memoization monad.
30+type MonadMemo s m = MonadState (MemoState s) m
31+
32+memoStateLookup
33+ :: forall k v s. (Typeable k, Typeable v, Ord k)
34+ => k -> MemoState s -> Maybe (Progress v)
35+memoStateLookup k (MemoState _ t) = case T.lookup @(k, v) t of
36+ Just (MemoMap m) -> M.lookup k m
37+ Nothing -> Nothing
38+
39+memoStateInsert
40+ :: forall k v s. (Typeable k, Typeable v, Ord k)
41+ => k -> Progress v -> MemoState s -> MemoState s
42+memoStateInsert k x (MemoState s t) = MemoState s $ T.insert (MemoMap m'') t
43+ where
44+ m'' = M.insert k x m'
45+ m' = case T.lookup @(k, v) t of
46+ Just (MemoMap m) -> m
47+ Nothing -> M.empty
48+
49+modifyMemoNonce :: MonadMemo s m => (s -> s) -> m s
50+modifyMemoNonce f = do
51+ MemoState s t <- get
52+ put $ MemoState (f s) t
53+ pure s
54+
55+-- | Run a memoized computation.
56+runMemo :: Monad m => s -> StateT (MemoState s) m a -> m a
57+runMemo s = (`evalStateT` MemoState s T.empty)
58+
59+-- | A description of how to effectfully tie knots in type @v@, using the @m@
60+-- monad, and by sharing some @d@ data among the recursive instances.
61+data KnotTier v d m = KnotTier
62+ { onKnotFound :: m d -- ^ Create some data that will be connected to this knot
63+ , onKnotUsed :: d -> m v -- ^ This is what the knot will look like as a value
64+ -- to the inner computations
65+ , tieKnot :: d -> v -> m v -- ^ Once we're done and we're outside, tie the
66+ -- knot using the datum
67+ }
68+
69+unknot :: KnotTier v Void m
70+unknot = KnotTier
71+ { onKnotFound = error "Recursion detected"
72+ , onKnotUsed = absurd
73+ , tieKnot = absurd
74+ }
75+
76+-- | Run a potentially recursive computation. The provided key will be used to
77+-- refer to the result of this computation. If during the computation, another
78+-- attempt to run the computation with the same key is made, we run a
79+-- tying-the-knot procedure.
80+--
81+-- If another attempt to run the computation with the same key is made
82+-- *after we're done*, we will return the memoized value.
83+memoWithKnot
84+ :: forall k v d m s.
85+ (Typeable k, Typeable v, Typeable d, Ord k, MonadMemo s m)
86+ => KnotTier v d m
87+ -> m v -- ^ the computation to memoize
88+ -> k -- ^ key for memoization
89+ -> m v
90+memoWithKnot tier f k = memoStateLookup @k @v k <$> get >>= \case
91+ Just (Finished v) -> pure v
92+ Just Started -> do
93+ d <- onKnotFound tier
94+ modify $ memoStateInsert @k @v k (TyingKnot $ toDyn d)
95+ onKnotUsed tier d
96+ Just (TyingKnot dyn) -> case fromDynamic dyn of
97+ Just d -> onKnotUsed tier d
98+ Nothing -> error $ "Type mismatch when examining the knot of "
99+ <> show (typeRep @(k -> v)) <> ": expected " <> show (typeRep @d)
100+ <> ", got " <> show (dynTypeRep dyn)
101+ Nothing -> do
102+ modify $ memoStateInsert @k @v k Started
103+ v <- f
104+ v' <- memoStateLookup @k @v k <$> get >>= \case
105+ Just Started -> pure v
106+ Just (TyingKnot dyn) -> case fromDynamic dyn of
107+ Just d -> tieKnot tier d v
108+ Nothing -> error $ "Type mismatch when tying the knot of "
109+ <> show (typeRep @(k -> v)) <> ": expected " <> show (typeRep @d)
110+ <> ", got " <> show (dynTypeRep dyn)
111+ Just (Finished _) -> error $ "Unexpected Finished when memoizing "
112+ <> show (typeRep @(k -> v))
113+ Nothing -> pure v
114+ -- Normally this would be an error, but the underlying monad can refuse
115+ -- to remember memoization state
116+ modify $ memoStateInsert @k @v k (Finished v')
117+ pure v'
118+
119+-- | Disambiguate memoized computations with an arbitrary tag.
120+memoTaggedWithKnot
121+ :: forall t k v d m s.
122+ ( Typeable t, Typeable k, Typeable v, Typeable d
123+ , Ord k, MonadMemo s m )
124+ => KnotTier v d m -> m v -> k -> m v
125+memoTaggedWithKnot tier f k = withTypeable (typeRepKind $ typeRep @t) $
126+ memoWithKnot tier f (Tagged @t k)
src/Data/OpenApi/Compare/Orphans.hs → src/OpenAPI/Checker/Orphans.hsrenamed+1−1
@@ -1,6 +1,6 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Orphans () where
3+module OpenAPI.Checker.Orphans () where
44
55import Control.Comonad.Env
66import qualified Data.HashMap.Strict.InsOrd as IOHM
src/Data/OpenApi/Compare/Paths.hs → src/OpenAPI/Checker/Paths.hsrenamed+10−10
@@ -1,18 +1,18 @@
11-- | Utilities for traversing heterogeneous trees. A heterogeneous tree is a
22-- collection of datatypes that "contain" eachother in some form of tree
33-- structure.
4−module Data.OpenApi.Compare.Paths
5− ( NiceQuiver,
6− AdditionalQuiverConstraints,
7− Paths (..),
8− DiffPaths (..),
9− catDiffPaths,
10− AnItem (..),
11− step,
4+module OpenAPI.Checker.Paths
5+ ( NiceQuiver
6+ , AdditionalQuiverConstraints
7+ , Paths (..)
8+ , DiffPaths (..)
9+ , catDiffPaths
10+ , AnItem (..)
11+ , step
1212
1313 -- * Reexports
14− (>>>),
15− (<<<),
14+ , (>>>)
15+ , (<<<)
1616 )
1717where
1818
src/Data/OpenApi/Compare/PathsPrefixTree.hs → src/OpenAPI/Checker/PathsPrefixTree.hsrenamed+42−65
@@ -1,42 +1,39 @@
11{-# LANGUAGE QuantifiedConstraints #-}
22
3−module Data.OpenApi.Compare.PathsPrefixTree
4− ( PathsPrefixTree (PathsPrefixNode),
5− AStep (..),
6− empty,
7− singleton,
8− fromList,
9− null,
10− foldWith,
11− toList,
12− filter,
13− filterWithKey,
14− takeSubtree,
15− lookup,
16− embed,
17− size,
18− partition,
19− map,
3+module OpenAPI.Checker.PathsPrefixTree
4+ ( PathsPrefixTree (PathsPrefixNode)
5+ , AStep (..)
6+ , empty
7+ , singleton
8+ , fromList
9+ , null
10+ , foldWith
11+ , toList
12+ , filter
13+ , takeSubtree
14+ , embed
15+ , size
16+ , partition
17+ , map
2018 )
2119where
2220
2321import Control.Monad
2422import Data.Aeson
2523import Data.Foldable hiding (null, toList)
26−import qualified Data.Aeson.Key as Key
27−import qualified Data.Aeson.KeyMap as KeyMap
24+import qualified Data.HashMap.Strict as HM
2825import Data.Kind
2926import qualified Data.Map as M
3027import Data.Monoid
31−import Data.OpenApi.Compare.Paths
3228import qualified Data.Set as S
3329import qualified Data.Text as T
3430import Data.Type.Equality
3531import qualified Data.TypeRepMap as TRM
3632import qualified Data.Vector as V
3733import qualified GHC.Exts as Exts
34+import OpenAPI.Checker.Paths
3835import Type.Reflection
39−import Prelude hiding (filter, lookup, map, null)
36+import Prelude hiding (filter, map, null)
4037
4138-- | A list of @AnItem r f@, but optimized into a prefix tree.
4239data PathsPrefixTree (q :: k -> k -> Type) (f :: k -> Type) (r :: k) = PathsPrefixTree
@@ -59,27 +56,10 @@ filter f (PathsPrefixTree roots branches) = PathsPrefixTree roots' branches'
5956 roots' = filterASet f roots
6057 branches' =
6158 Exts.fromList
62− . fmap (\(TRM.WrapTypeable (AStep x)) -> TRM.WrapTypeable . AStep . M.mapMaybe (maybeNonEmpty . filter f) $ x)
59+ . fmap (\(TRM.WrapTypeable (AStep x)) -> TRM.WrapTypeable . AStep . M.filter (not . null) . fmap (filter f) $ x)
6360 . Exts.toList
6461 $ branches
6562
66− maybeNonEmpty = mfilter (not . null) . Just
67−
68−filterWithKey :: (forall a. Paths q r a -> f a -> Bool) -> PathsPrefixTree q f r -> PathsPrefixTree q f r
69−filterWithKey = go Root
70− where
71− go :: Paths q r b -> (forall a. Paths q r a -> f a -> Bool) -> PathsPrefixTree q f b -> PathsPrefixTree q f b
72− go xs f (PathsPrefixTree roots branches) = PathsPrefixTree roots' branches'
73− where
74− roots' = filterASet (f xs) roots
75− branches' =
76− Exts.fromList
77− . fmap (\(TRM.WrapTypeable (AStep x)) -> TRM.WrapTypeable . AStep . M.mapMaybeWithKey (\k -> maybeNonEmpty . go (xs `Snoc` k) f) $ x)
78− . Exts.toList
79− $ branches
80−
81− maybeNonEmpty = mfilter (not . null) . Just
82−
8363-- | The number of leaves.
8464size :: PathsPrefixTree q f r -> Int
8565size (PathsPrefixTree root branches) =
@@ -103,11 +83,11 @@ deriving stock instance Eq (PathsPrefixTree q f a)
10383
10484-- Kind of orphan. Treat the map as an infinite tuple of @Maybe (f a)@'s, where
10585-- the components are ordered by the @SomeTypeRep@ of the @a@.
106−compareTRM ::
107− (forall a. Typeable a => Ord (f a)) =>
108− TRM.TypeRepMap f ->
109− TRM.TypeRepMap f ->
110− Ordering
86+compareTRM
87+ :: (forall a. Typeable a => Ord (f a))
88+ => TRM.TypeRepMap f
89+ -> TRM.TypeRepMap f
90+ -> Ordering
11191compareTRM s1 s2 =
11292 foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
11393 where
@@ -117,11 +97,11 @@ compareTRM s1 s2 =
11797 compareMaybe _ Nothing (Just _) = LT
11898 compareMaybe _ (Just _) Nothing = GT
11999 compareMaybe cmp (Just x) (Just y) = cmp x y
120− compareW ::
121− (forall a. Typeable a => Ord (f a)) =>
122− TRM.WrapTypeable f ->
123− TRM.WrapTypeable f ->
124− Ordering
100+ compareW
101+ :: (forall a. Typeable a => Ord (f a))
102+ => TRM.WrapTypeable f
103+ -> TRM.WrapTypeable f
104+ -> Ordering
125105 compareW (TRM.WrapTypeable (x :: f a)) (TRM.WrapTypeable (y :: f b))
126106 | Just Refl <- testEquality (typeRep @a) (typeRep @b) = compare x y
127107 | otherwise = EQ -- unreachable
@@ -173,10 +153,10 @@ instance Monoid (ASet a) where
173153 mempty = AnEmptySet
174154
175155data AStep (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: k) where
176− AStep ::
177− NiceQuiver q r a =>
178− !(M.Map (q r a) (PathsPrefixTree q f a)) ->
179− AStep q f r a
156+ AStep
157+ :: NiceQuiver q r a =>
158+ !(M.Map (q r a) (PathsPrefixTree q f a))
159+ -> AStep q f r a
180160
181161mapAStep :: (forall x. f x -> f x) -> AStep q f r a -> AStep q f r a
182162mapAStep f (AStep m) = AStep $ M.map (map f) m
@@ -216,12 +196,12 @@ null :: PathsPrefixTree q f r -> Bool
216196null (PathsPrefixTree AnEmptySet s) = all (\(TRM.WrapTypeable (AStep x)) -> all null x) (Exts.toList s)
217197null _ = False
218198
219−foldWith ::
220− forall q f m r.
221− Monoid m =>
222− (forall a. Ord (f a) => Paths q r a -> f a -> m) ->
223− PathsPrefixTree q f r ->
224− m
199+foldWith
200+ :: forall q f m r.
201+ Monoid m
202+ => (forall a. Ord (f a) => Paths q r a -> f a -> m)
203+ -> PathsPrefixTree q f r
204+ -> m
225205foldWith k = goTPT Root
226206 where
227207 goTPT :: forall a. Paths q r a -> PathsPrefixTree q f a -> m
@@ -245,9 +225,6 @@ takeSubtree (Snoc xs x) t =
245225 foldMap (\(AStep m) -> fold $ M.lookup x m) $
246226 TRM.lookup @a $ snocItems $ takeSubtree xs t
247227
248−lookup :: Paths q r a -> PathsPrefixTree q f r -> S.Set (f a)
249−lookup xs = toSet . rootItems . takeSubtree xs
250−
251228-- | Embed a subtree in a larger tree with given prefix
252229embed :: Paths q r a -> PathsPrefixTree q f a -> PathsPrefixTree q f r
253230embed Root t = t
@@ -257,7 +234,7 @@ newtype MergableObject = MergableObject {getMergableObject :: Object}
257234
258235instance Semigroup MergableObject where
259236 (MergableObject x) <> (MergableObject y) =
260− MergableObject $ KeyMap.unionWith mergeValue x y
237+ MergableObject $ HM.unionWith mergeValue x y
261238 where
262239 mergeValue :: Value -> Value -> Value
263240 mergeValue (Object a) (Object b) =
@@ -272,6 +249,6 @@ instance Monoid MergableObject where
272249
273250traceObject :: Paths q r a -> Value -> Object
274251traceObject Root (Object o) = o
275−traceObject Root v = KeyMap.singleton "root" v
252+traceObject Root v = HM.singleton "root" v
276253traceObject (root `Snoc` s) v =
277− traceObject root . Object $ KeyMap.singleton (Key.fromText . T.pack . show $ s) v
254+ traceObject root . Object $ HM.singleton (T.pack . show $ s) v
src/Data/OpenApi/Compare/References.hs → src/OpenAPI/Checker/References.hsrenamed+12−11
@@ -1,9 +1,9 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.References
4− ( Step (..),
5− dereference,
6− Typeable,
3+module OpenAPI.Checker.References
4+ ( Step (..)
5+ , dereference
6+ , Typeable
77 )
88where
99
@@ -11,18 +11,19 @@ import Data.HList
1111import qualified Data.HashMap.Strict.InsOrd as IOHM
1212import Data.Maybe
1313import Data.OpenApi
14−import Data.OpenApi.Compare.Orphans ()
15−import Data.OpenApi.Compare.Subtree
14+import Data.Typeable
15+import OpenAPI.Checker.Orphans ()
16+import OpenAPI.Checker.Subtree
1617
1718instance Typeable a => Steppable (Referenced a) a where
1819 data Step (Referenced a) a = InlineStep
1920 deriving stock (Eq, Ord, Show)
2021
21−dereference ::
22− Typeable a =>
23− Traced (Definitions a) ->
24− Traced (Referenced a) ->
25− Traced a
22+dereference
23+ :: Typeable a
24+ => Traced (Definitions a)
25+ -> Traced (Referenced a)
26+ -> Traced a
2627dereference defs x = case extract x of
2728 Inline a ->
2829 traced (ask x >>> step InlineStep) a
src/Data/OpenApi/Compare/Report.hs → src/OpenAPI/Checker/Report.hsrenamed+119−176
@@ -1,13 +1,11 @@
1−module Data.OpenApi.Compare.Report
2− ( generateReport,
3− CheckerOutput (..),
4− ReportInput (..),
5− segregateIssues,
6− ReportStatus (..),
7− Pandoc,
8− ReportConfig (..),
9− ReportTreeStyle (..),
10− ReportMode (..),
1+module OpenAPI.Checker.Report
2+ ( generateReport
3+ , ReportInput (..)
4+ , ReportStatus (..)
5+ , Pandoc
6+ , ReportConfig (..)
7+ , ReportTreeStyle (..)
8+ , ReportMode (..)
119 )
1210where
1311
@@ -23,15 +21,6 @@ import qualified Data.Map as M
2321import Data.Map.Ordered (OMap)
2422import qualified Data.Map.Ordered as OM
2523import Data.Maybe
26−import Data.OpenApi.Compare.Behavior
27−import Data.OpenApi.Compare.Paths
28−import Data.OpenApi.Compare.PathsPrefixTree hiding (empty)
29−import qualified Data.OpenApi.Compare.PathsPrefixTree as P hiding (empty)
30−import Data.OpenApi.Compare.Report.Jet
31−import Data.OpenApi.Compare.Subtree (invertIssueOrientationP)
32−import Data.OpenApi.Compare.Validate.OpenApi
33−import Data.OpenApi.Compare.Validate.Schema.Issues
34−import Data.OpenApi.Compare.Validate.Schema.TypedJson
3524import Data.OpenUnion
3625import Data.OpenUnion.Extra
3726import Data.Set
@@ -41,47 +30,27 @@ import qualified Data.Text as T
4130import Data.TypeRepMap hiding (empty)
4231import Data.Typeable
4332import Generic.Data
33+import OpenAPI.Checker.Behavior
34+import OpenAPI.Checker.Paths
35+import OpenAPI.Checker.PathsPrefixTree hiding (empty)
36+import qualified OpenAPI.Checker.PathsPrefixTree as P hiding (empty)
37+import OpenAPI.Checker.Report.Jet
38+import OpenAPI.Checker.Subtree (invertIssueOrientationP)
39+import OpenAPI.Checker.Validate.OpenApi
40+import OpenAPI.Checker.Validate.Schema.TypedJson
41+import OpenAPI.Checker.Validate.Schema.Issues
4442import Text.Pandoc.Builder
4543
4644type Changes = P.PathsPrefixTree Behave AnIssue 'APILevel
4745
48−data CheckerOutput = CheckerOutput
49− { forwardChanges :: Changes
50− , backwardChanges :: Changes
51− }
52− deriving stock (Generic)
53− deriving (Semigroup, Monoid) via (Generically CheckerOutput)
54− deriving anyclass (ToJSON)
55−
5646data ReportInput = ReportInput
57− { -- | forward 'CertainIssue', 'ProbablyIssue' and 'Comment'
58− breakingChanges :: Changes
59− , -- | backward 'CertainIssue', 'ProbablyIssue' and 'Comment', except those shadowed by 'relatedIssues'
60− nonBreakingChanges :: Changes
61− , -- | forward and backward 'Unsupported' (assumed to be the same anyway)
62− unsupportedChanges :: Changes
63− , -- | forward and backward 'SchemaInvalid' (assumed to be the same anyway)
64− schemaIssues :: Changes
47+ { breakingChanges :: Changes
48+ , nonBreakingChanges :: Changes
6549 }
6650 deriving stock (Generic)
6751 deriving (Semigroup, Monoid) via (Generically ReportInput)
6852 deriving anyclass (ToJSON)
6953
70−segregateIssues :: CheckerOutput -> ReportInput
71−segregateIssues CheckerOutput {forwardChanges = fwd, backwardChanges = bck} =
72− ReportInput
73− { breakingChanges = P.filter isBreaking fwd
74− , nonBreakingChanges = invertIssueOrientationP $ P.filterWithKey isNonBreaking bck
75− , unsupportedChanges = P.filter isUnsupported fwd <> P.filter isUnsupported bck
76− , schemaIssues = P.filter isSchemaIssue fwd <> P.filter isSchemaIssue bck
77− }
78− where
79− isBreaking i = anIssueKind i `elem` [CertainIssue, ProbablyIssue, Comment]
80− isNonBreaking :: Paths Behave 'APILevel a -> AnIssue a -> Bool
81− isNonBreaking xs i = isBreaking i && all (\j -> not $ relatedAnIssues i j) (P.lookup xs fwd)
82− isUnsupported i = anIssueKind i == Unsupported
83− isSchemaIssue i = anIssueKind i == SchemaInvalid
84−
8554data ReportStatus
8655 = BreakingChanges
8756 | NoBreakingChanges
@@ -109,66 +78,49 @@ data ReportTreeStyle = HeadersTreeStyle | FoldingBlockquotesTreeStyle
10978twoRowTable :: [(Inlines, Inlines)] -> Blocks
11079twoRowTable x = simpleTable (para . fst <$> x) [para . snd <$> x]
11180
112−generateReport :: ReportConfig -> ReportInput -> (Blocks, ReportStatus)
81+generateReport :: ReportConfig -> ReportInput -> (Pandoc, ReportStatus)
11382generateReport cfg inp =
114− let schemaIssuesPresent = not $ P.null $ schemaIssues inp
115− breakingChangesPresent = not $ P.null $ breakingChanges inp
116− nonBreakingChangesPresent = not $ P.null $ nonBreakingChanges inp
117− unsupportedChangesPresent = not $ P.null $ unsupportedChanges inp
83+ let partitionUnsupported = P.partition (\(AnIssue _ i) -> issueIsUnsupported i)
84+ (bUnsupported, breaking) = partitionUnsupported $ breakingChanges inp
85+ (nbUnsupported, invertIssueOrientationP -> nonBreaking) = partitionUnsupported $ nonBreakingChanges inp
86+ unsupported = bUnsupported <> nbUnsupported
87+ breakingChangesPresent = not $ P.null breaking
88+ nonBreakingChangesPresent = not $ P.null nonBreaking
89+ unsupportedChangesPresent = not $ P.null unsupported
11890 nonBreakingChangesShown = case reportMode cfg of
11991 All -> True
12092 OnlyErrors -> False
12193 builder = buildReport cfg
12294 report =
123− header 1 "Summary"
124− <> twoRowTable
125− ( when'
126− schemaIssuesPresent
127− [
128− ( refOpt schemaIssuesPresent schemaIssuesId "‼️ Schema issues"
129− , show' $ P.size $ schemaIssues inp
130− )
131− ]
132− ++ [
133− ( refOpt breakingChangesPresent breakingChangesId "❌ Breaking changes"
134− , show' $ P.size $ breakingChanges inp
95+ doc $
96+ header 1 "Summary"
97+ <> twoRowTable
98+ ([ ( refOpt breakingChangesPresent breakingChangesId "⚠️ Breaking changes"
99+ , show' $ P.size breaking
100+ )
101+ ]
102+ ++ when'
103+ nonBreakingChangesShown
104+ [ ( refOpt nonBreakingChangesPresent nonBreakingChangesId "🙆 Non-breaking changes"
105+ , show' $ P.size nonBreaking
135106 )
136107 ]
137− ++ when'
138− nonBreakingChangesShown
139− [
140− ( refOpt nonBreakingChangesPresent nonBreakingChangesId "⚠️ Non-breaking changes"
141− , show' $ P.size $ nonBreakingChanges inp
142− )
143− ]
144− ++ when'
145− unsupportedChangesPresent
146− [
147− ( refOpt unsupportedChangesPresent unsupportedChangesId "❓ Unsupported feature changes"
148− , show' $ P.size $ unsupportedChanges inp
149− )
150− ]
151− )
152− <> when'
153− schemaIssuesPresent
154− ( header 1 (anchor schemaIssuesId <> "‼️ Schema issues")
155− <> builder (showErrs $ schemaIssues inp)
156− )
157− <> when'
158− breakingChangesPresent
159− ( header 1 (anchor breakingChangesId <> "❌ Breaking changes")
160− <> builder (showErrs $ breakingChanges inp)
161− )
162− <> when'
163− (nonBreakingChangesPresent && nonBreakingChangesShown)
164− ( header 1 (anchor nonBreakingChangesId <> "⚠️ Non-breaking changes")
165− <> builder (showErrs $ nonBreakingChanges inp)
166− )
167− <> when'
168− unsupportedChangesPresent
169− ( header 1 (anchor unsupportedChangesId <> "❓ Unsupported feature changes")
170− <> builder (showErrs $ unsupportedChanges inp)
171− )
108+ ++ [ ( refOpt unsupportedChangesPresent unsupportedChangesId "🤷 Unsupported feature changes"
109+ , show' $ P.size unsupported
110+ )
111+ ])
112+ <> when'
113+ breakingChangesPresent
114+ (header 1 (anchor breakingChangesId <> "⚠️ Breaking changes")
115+ <> builder (showErrs breaking))
116+ <> when'
117+ (nonBreakingChangesPresent && nonBreakingChangesShown)
118+ (header 1 (anchor nonBreakingChangesId <> "🙆 Non-breaking changes")
119+ <> builder (showErrs nonBreaking))
120+ <> when'
121+ unsupportedChangesPresent
122+ (header 1 (anchor unsupportedChangesId <> "🤷 Unsupported feature changes")
123+ <> builder (showErrs unsupported))
172124 status =
173125 if
174126 | breakingChangesPresent -> BreakingChanges
@@ -183,11 +135,10 @@ generateReport cfg inp =
183135 refOpt False _ i = i
184136 refOpt True a i = link ("#" <> a) "" i
185137
186− breakingChangesId, nonBreakingChangesId, unsupportedChangesId, schemaIssuesId :: Text
138+ breakingChangesId, nonBreakingChangesId, unsupportedChangesId :: Text
187139 breakingChangesId = "breaking-changes"
188140 unsupportedChangesId = "unsupported-changes"
189141 nonBreakingChangesId = "non-breaking-changes"
190− schemaIssuesId = "schema-issues"
191142
192143 when' :: Monoid m => Bool -> m -> m
193144 when' True m = m
@@ -202,10 +153,9 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
202153 Just Refl
203154 | (S.toList -> p@((AnIssue ori _) : _), o) <-
204155 S.partition
205− ( \((AnIssue _ u)) -> case u of
206− NoPathsMatched {} -> True
207− AllPathsFailed {} -> True
208− )
156+ (\((AnIssue _ u)) -> case u of
157+ NoPathsMatched {} -> True
158+ AllPathsFailed {} -> True)
209159 currentIssues ->
210160 let p' = p <&> (\(AnIssue _ i) -> i)
211161 in (Just (ori, p'), o)
@@ -216,10 +166,9 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
216166 paths = case removedPaths of
217167 Just (ori, ps) -> do
218168 singletonHeader
219− ( case ori of
220− Forward -> "Removed paths"
221− Backward -> "Added paths"
222− )
169+ (case ori of
170+ Forward -> "Removed paths"
171+ Backward -> "Added paths")
223172 $ singletonBody $
224173 bulletList $
225174 ps <&> \case
@@ -231,7 +180,7 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
231180 flip foldMap (M.toList m) $ \(bhv, subErrors) ->
232181 if P.null subErrors
233182 then mempty
234− else singletonHeader (describeBehavior bhv) $ showErrs subErrors
183+ else singletonHeader (describeBehaviour bhv) $ showErrs subErrors
235184 in issues <> paths <> rest
236185
237186unfoldFunctions :: forall m a. (Monoid m, Eq a) => a -> [a -> (m, a)] -> (a -> m) -> m
@@ -249,68 +198,66 @@ jets =
249198 unwrapReportJetResult
250199 <$> [ constructReportJet $
251200 curry $ \case
252− (OfType Object, p@(InPartition _)) -> Just $ describeBehavior p :: Maybe Inlines
201+ (OfType Object, p@(InPartition _)) -> Just $ describeBehaviour p :: Maybe Inlines
253202 _ -> Nothing
254203 , constructReportJet jsonPathJet
255204 , constructReportJet $ \p@(AtPath _) op@(InOperation _) ->
256− strong (describeBehavior op) <> " " <> describeBehavior p :: Inlines
205+ strong (describeBehaviour op) <> " " <> describeBehaviour p :: Inlines
257206 , constructReportJet $ \(WithStatusCode c) ResponsePayload PayloadSchema ->
258− "⬅️☁️ JSON Response – " <> str (T.pack . show $ c) :: Inlines
259− , constructReportJet $ \InRequest InPayload PayloadSchema -> "➡️☁️ JSON Request" :: Inlines
207+ "📱⬅️ JSON Response – " <> str (T.pack . show $ c) :: Inlines
208+ , constructReportJet $ \InRequest InPayload PayloadSchema -> "📱➡️ JSON Request" :: Inlines
260209 ]
261210 where
262211 unwrapReportJetResult :: ReportJetResult Behave x -> ReportJet' Behave x
263212 unwrapReportJetResult (Pure _) = error "There really shouldn't be any results here."
264213 unwrapReportJetResult (Free f) = f
265214
266− jsonPathJet ::
267− NonEmpty
268− ( Union
269− '[ Behave 'SchemaLevel 'TypedSchemaLevel
270− , Behave 'TypedSchemaLevel 'SchemaLevel
271− ]
272− ) ->
273− Inlines
215+ jsonPathJet
216+ :: NonEmpty
217+ ( Union
218+ '[ Behave 'SchemaLevel 'TypedSchemaLevel
219+ , Behave 'TypedSchemaLevel 'SchemaLevel
220+ ]
221+ )
222+ -> Inlines
274223 jsonPathJet x = code $ "$" <> showParts (NE.toList x)
275224 where
276− showParts ::
277− [ Union
278− '[ Behave 'SchemaLevel 'TypedSchemaLevel
279− , Behave 'TypedSchemaLevel 'SchemaLevel
280− ]
281− ] ->
282− Text
225+ showParts
226+ :: [ Union
227+ '[ Behave 'SchemaLevel 'TypedSchemaLevel
228+ , Behave 'TypedSchemaLevel 'SchemaLevel
229+ ]
230+ ]
231+ -> Text
283232 showParts [] = mempty
284233 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion (InProperty _)) : _)) = showParts xs
285234 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion InAdditionalProperty) : _)) = showParts xs
286235 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion InItems : _)) = showParts xs
287236 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion (InItem _) : _)) = showParts xs
288237 showParts (y : ys) =
289− ( (\(OfType t) -> "(" <> describeJSONType t <> ")")
290− @@> ( \case
291− InItems -> "[*]"
292− InItem i -> "[" <> T.pack (show i) <> "]"
293− InProperty p -> "." <> p
294− InAdditionalProperty -> ".*"
295− )
296− @@> typesExhausted
297− )
238+ ((\(OfType t) -> "(" <> describeJSONType t <> ")")
239+ @@> (\case
240+ InItems -> "[*]"
241+ InItem i -> "[" <> T.pack (show i) <> "]"
242+ InProperty p -> "." <> p
243+ InAdditionalProperty -> ".*")
244+ @@> typesExhausted)
298245 y
299246 <> showParts ys
300247
301−observeJetShowErrs ::
302− ReportJet' Behave (Maybe Inlines) ->
303− P.PathsPrefixTree Behave AnIssue a ->
304− (Report, P.PathsPrefixTree Behave AnIssue a)
248+observeJetShowErrs
249+ :: ReportJet' Behave (Maybe Inlines)
250+ -> P.PathsPrefixTree Behave AnIssue a
251+ -> (Report, P.PathsPrefixTree Behave AnIssue a)
305252observeJetShowErrs jet p = case observeJetShowErrs' jet p of
306253 Just m -> m
307254 Nothing -> (mempty, p)
308255
309−observeJetShowErrs' ::
310− forall a.
311− ReportJet' Behave (Maybe Inlines) ->
312− P.PathsPrefixTree Behave AnIssue a ->
313− Maybe (Report, P.PathsPrefixTree Behave AnIssue a)
256+observeJetShowErrs'
257+ :: forall a.
258+ ReportJet' Behave (Maybe Inlines)
259+ -> P.PathsPrefixTree Behave AnIssue a
260+ -> Maybe (Report, P.PathsPrefixTree Behave AnIssue a)
314261observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues) =
315262 let results =
316263 subIssues >>= \(WrapTypeable (AStep m)) ->
@@ -318,23 +265,21 @@ observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues)
318265 maybe (Left $ embed (step bhv) subErrs) Right . listToMaybe $
319266 jet @_ @_ @[] bhv
320267 & mapMaybe
321− ( \case
322− Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
323− Pure (Just h) ->
324− if P.null subErrs
325− then Just mempty
326− else Just (singletonHeader h (showErrs subErrs), mempty)
327− Pure Nothing -> Nothing
328− )
268+ (\case
269+ Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
270+ Pure (Just h) ->
271+ if P.null subErrs
272+ then Just mempty
273+ else Just (singletonHeader h (showErrs subErrs), mempty)
274+ Pure Nothing -> Nothing)
329275 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
330276 if any isRight results
331277 then
332278 Just $
333279 foldMap
334− ( \case
335− Left e -> (mempty, e)
336− Right m -> m
337− )
280+ (\case
281+ Left e -> (mempty, e)
282+ Right m -> m)
338283 results
339284 else Nothing
340285
@@ -357,9 +302,8 @@ buildReport cfg = case treeStyle cfg of
357302 body rprt
358303 <> foldOMapWithKey
359304 (headers rprt)
360− ( \k v ->
361− header level k <> subBuilder v
362− )
305+ (\k v ->
306+ header level k <> subBuilder v)
363307 where
364308 subBuilder = headerStyleBuilder (level + 1)
365309
@@ -368,17 +312,16 @@ buildReport cfg = case treeStyle cfg of
368312 body rprt
369313 <> foldOMapWithKey
370314 (headers rprt)
371− ( \k v ->
372− if (OM.size . headers $ rprt) < 2
373− then para k <> blockQuote (subBuilder v)
374− else
375− rawHtml "<details>"
376− <> rawHtml "<summary>"
377− <> plain k
378− <> rawHtml "</summary>"
379− <> blockQuote (subBuilder v)
380− <> rawHtml "</details>"
381− )
315+ (\k v ->
316+ if (OM.size . headers $ rprt) < 2
317+ then para k <> blockQuote (subBuilder v)
318+ else
319+ rawHtml "<details>"
320+ <> rawHtml "<summary>"
321+ <> plain k
322+ <> rawHtml "</summary>"
323+ <> blockQuote (subBuilder v)
324+ <> rawHtml "</details>")
382325 where
383326 subBuilder = foldingStyleBuilder
384327
src/Data/OpenApi/Compare/Report/Html/Template.hs → src/OpenAPI/Checker/Report/Html/Template.hsrenamed+5−5
@@ -1,7 +1,7 @@
11{-# LANGUAGE TemplateHaskell #-}
22
3−module Data.OpenApi.Compare.Report.Html.Template
4− ( template,
3+module OpenAPI.Checker.Report.Html.Template
4+ ( template
55 )
66where
77
@@ -24,11 +24,11 @@ template =
2424 \<meta charset=\"utf-8\">\
2525 \<title></title>\
2626 \<meta name=\"description\" content=\"\">\
27− \<meta name=\"generator\" content=\"CompaREST\" />\
27+ \<meta name=\"generator\" content=\"OpenApi Diff\" />\
2828 \<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\
2929 \</head>\
3030 \<body>\
31− \<header><h1>CompaREST</h1></header>\
31+ \<header><h1>⛄ OpenApi Diff</h1></header>\
3232 \<main>\
3333 \$body$\
3434 \</main>\
@@ -36,4 +36,4 @@ template =
3636 \</html>"
3737
3838awsmCss :: ByteString
39−awsmCss = $(makeRelativeToProject "css/awsm.min.css" >>= embedFile)
39+awsmCss = $(makeRelativeToProject "awsm-css/dist/awsm.min.css" >>= embedFile)
src/Data/OpenApi/Compare/Report/Jet.hs → src/OpenAPI/Checker/Report/Jet.hsrenamed+6−6
@@ -1,8 +1,8 @@
1−module Data.OpenApi.Compare.Report.Jet
2− ( ReportJet (..),
3− ReportJet',
4− ConstructReportJet (..),
5− ReportJetResult,
1+module OpenAPI.Checker.Report.Jet
2+ ( ReportJet (..)
3+ , ReportJet'
4+ , ConstructReportJet (..)
5+ , ReportJetResult
66 )
77where
88
@@ -23,7 +23,7 @@ import Text.Pandoc.Builder
2323-- The term "jet" in this context was introduced in the Urbit project:
2424-- https://urbit.org/docs/vere/jetting/
2525--
26−-- The pattern fits well for simplifying 'Behavior' tree paths.
26+-- The pattern fits well for simplifying 'Behaviour' tree paths.
2727class ConstructReportJet x f where
2828 constructReportJet :: x -> ReportJetResult f (Maybe Inlines)
2929
src/OpenAPI/Checker/Run.hsnew file+30
@@ -0,0 +1,30 @@
1+module OpenAPI.Checker.Run
2+ ( runChecker
3+ , runReport
4+ , module OpenAPI.Checker.Report
5+ )
6+where
7+
8+import Data.HList
9+import Data.OpenApi (OpenApi)
10+import OpenAPI.Checker.Paths
11+import OpenAPI.Checker.Report
12+import OpenAPI.Checker.Subtree
13+import OpenAPI.Checker.Validate.OpenApi ()
14+
15+runChecker :: (OpenApi, OpenApi) -> ReportInput
16+runChecker (client, server) =
17+ ReportInput
18+ { breakingChanges = run client server
19+ , nonBreakingChanges = run server client
20+ }
21+ where
22+ toPC p c =
23+ ProdCons
24+ { producer = traced (step ClientSchema) p
25+ , consumer = traced (step ServerSchema) c
26+ }
27+ run p c = either id mempty . runCompatFormula . checkCompatibility Root HNil $ toPC p c
28+
29+runReport :: ReportConfig -> (OpenApi, OpenApi) -> (Pandoc, ReportStatus)
30+runReport cfg = generateReport cfg . runChecker
src/Data/OpenApi/Compare/Subtree.hs → src/OpenAPI/Checker/Subtree.hsrenamed+125−132
@@ -1,52 +1,51 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Subtree
4− ( Steppable (..),
5− Step (..),
6− TraceRoot,
7− Trace,
8− Traced,
9− Traced',
10− pattern Traced,
11− traced,
12− retraced,
13− stepTraced,
14− Subtree (..),
15− checkCompatibility,
16− checkSubstructure,
17− CompatM (..),
18− CompatFormula',
19− SemanticCompatFormula,
20− ProdCons (..),
21− orientProdCons,
22− swapProdCons,
23− runCompatFormula,
24− issueAt,
25− anItem,
26− anIssue,
27− invertIssueOrientation,
28− invertIssueOrientationP,
29− embedFormula,
30− anyOfAt,
31− clarifyIssue,
32− structuralIssue,
3+module OpenAPI.Checker.Subtree
4+ ( Steppable (..)
5+ , Step (..)
6+ , TraceRoot
7+ , Trace
8+ , Traced
9+ , Traced'
10+ , pattern Traced
11+ , traced
12+ , retraced
13+ , stepTraced
14+ , Subtree (..)
15+ , checkCompatibility
16+ , checkSubstructure
17+ , CompatM (..)
18+ , CompatFormula'
19+ , SemanticCompatFormula
20+ , ProdCons (..)
21+ , swapProdCons
22+ , runCompatFormula
23+ , issueAt
24+ , anItem
25+ , anIssue
26+ , invertIssueOrientation
27+ , invertIssueOrientationP
28+ , embedFormula
29+ , anyOfAt
30+ , clarifyIssue
31+ , structuralIssue
3332
3433 -- * Structural helpers
35− structuralMaybe,
36− structuralMaybeWith,
37− structuralEq,
38− iohmStructural,
39− iohmStructuralWith,
40− structuralList,
34+ , structuralMaybe
35+ , structuralMaybeWith
36+ , structuralEq
37+ , iohmStructural
38+ , iohmStructuralWith
39+ , structuralList
4140
4241 -- * Reexports
43− (>>>),
44− (<<<),
45− extract,
46− ask,
47− local,
48− step,
49− Typeable,
42+ , (>>>)
43+ , (<<<)
44+ , extract
45+ , ask
46+ , local
47+ , step
48+ , Typeable
5049 )
5150where
5251
@@ -60,13 +59,13 @@ import qualified Data.HashMap.Strict.InsOrd as IOHM
6059import Data.Hashable
6160import Data.Kind
6261import Data.OpenApi
63−import Data.OpenApi.Compare.Behavior
64−import Data.OpenApi.Compare.Formula
65−import Data.OpenApi.Compare.Memo
66−import Data.OpenApi.Compare.Paths
67−import qualified Data.OpenApi.Compare.PathsPrefixTree as P
6862import qualified Data.Set as S
6963import Data.Typeable
64+import OpenAPI.Checker.Behavior
65+import OpenAPI.Checker.Formula
66+import OpenAPI.Checker.Memo
67+import OpenAPI.Checker.Paths
68+import qualified OpenAPI.Checker.PathsPrefixTree as P
7069
7170class
7271 (Typeable Step, Typeable a, Typeable b, Ord (Step a b), Show (Step a b)) =>
@@ -111,14 +110,10 @@ data ProdCons a = ProdCons
111110 }
112111 deriving stock (Eq, Ord, Show, Functor, Foldable, Traversable)
113112
114−orientProdCons :: Orientation -> ProdCons x -> ProdCons x
115−orientProdCons Forward x = x
116−orientProdCons Backward (ProdCons p c) = ProdCons c p
117−
118−swapProdCons ::
119− SwapEnvRoles xs =>
120− (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a) ->
121− (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
113+swapProdCons
114+ :: SwapEnvRoles xs
115+ => (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
116+ -> (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
122117swapProdCons f e (ProdCons p c) =
123118 invertIssueOrientation $
124119 f (swapEnvRoles e) (ProdCons c p)
@@ -153,8 +148,8 @@ instance Applicative ProdCons where
153148 ProdCons fp fc <*> ProdCons xp xc = ProdCons (fp xp) (fc xc)
154149
155150newtype CompatM a = CompatM
156− { unCompatM ::
157− StateT (MemoState VarRef) Identity a
151+ { unCompatM
152+ :: StateT (MemoState VarRef) Identity a
158153 }
159154 deriving newtype
160155 ( Functor
@@ -183,64 +178,64 @@ class (Typeable t, Issuable (SubtreeLevel t)) => Subtree (t :: Type) where
183178 type CheckEnv t :: [Type]
184179 type SubtreeLevel t :: BehaviorLevel
185180
186− checkStructuralCompatibility ::
187− HList (CheckEnv t) ->
188− ProdCons (Traced t) ->
189− StructuralCompatFormula ()
181+ checkStructuralCompatibility
182+ :: HList (CheckEnv t)
183+ -> ProdCons (Traced t)
184+ -> StructuralCompatFormula ()
190185
191− checkSemanticCompatibility ::
192− HList (CheckEnv t) ->
193− Behavior (SubtreeLevel t) ->
194− ProdCons (Traced t) ->
195− SemanticCompatFormula ()
186+ checkSemanticCompatibility
187+ :: HList (CheckEnv t)
188+ -> Behavior (SubtreeLevel t)
189+ -> ProdCons (Traced t)
190+ -> SemanticCompatFormula ()
196191
197192{-# WARNING checkStructuralCompatibility "You should not be calling this directly. Use 'checkSubstructure'" #-}
198193
199194{-# WARNING checkSemanticCompatibility "You should not be calling this directly. Use 'checkCompatibility'" #-}
200195
201−checkCompatibility ::
202− forall t xs.
203− (ReassembleHList xs (CheckEnv t), Subtree t) =>
204− Behavior (SubtreeLevel t) ->
205− HList xs ->
206− ProdCons (Traced t) ->
207− SemanticCompatFormula ()
196+checkCompatibility
197+ :: forall t xs.
198+ (ReassembleHList xs (CheckEnv t), Subtree t)
199+ => Behavior (SubtreeLevel t)
200+ -> HList xs
201+ -> ProdCons (Traced t)
202+ -> SemanticCompatFormula ()
208203checkCompatibility bhv e = memo bhv SemanticMemoKey $ \pc ->
209204 case runCompatFormula $ checkSubstructure e pc of
210205 Left _ -> checkSemanticCompatibility (reassemble e) bhv pc
211206 Right () -> pure ()
212207{-# INLINE checkCompatibility #-}
213208
214−checkSubstructure ::
215− (ReassembleHList xs (CheckEnv t), Subtree t) =>
216− HList xs ->
217− ProdCons (Traced t) ->
218− StructuralCompatFormula ()
209+checkSubstructure
210+ :: (ReassembleHList xs (CheckEnv t), Subtree t)
211+ => HList xs
212+ -> ProdCons (Traced t)
213+ -> StructuralCompatFormula ()
219214checkSubstructure e = memo Root StructuralMemoKey $ checkStructuralCompatibility (reassemble e)
220215{-# INLINE checkSubstructure #-}
221216
222−structuralMaybe ::
223− (Subtree a, ReassembleHList xs (CheckEnv a)) =>
224− HList xs ->
225− ProdCons (Maybe (Traced a)) ->
226− StructuralCompatFormula ()
217+structuralMaybe
218+ :: (Subtree a, ReassembleHList xs (CheckEnv a))
219+ => HList xs
220+ -> ProdCons (Maybe (Traced a))
221+ -> StructuralCompatFormula ()
227222structuralMaybe e = structuralMaybeWith (checkSubstructure e)
228223{-# INLINE structuralMaybe #-}
229224
230−structuralMaybeWith ::
231− (ProdCons a -> StructuralCompatFormula ()) ->
232− ProdCons (Maybe a) ->
233− StructuralCompatFormula ()
225+structuralMaybeWith
226+ :: (ProdCons a -> StructuralCompatFormula ())
227+ -> ProdCons (Maybe a)
228+ -> StructuralCompatFormula ()
234229structuralMaybeWith f (ProdCons (Just a) (Just b)) = f $ ProdCons a b
235230structuralMaybeWith _ (ProdCons Nothing Nothing) = pure ()
236231structuralMaybeWith _ _ = structuralIssue
237232{-# INLINE structuralMaybeWith #-}
238233
239−structuralList ::
240− (Subtree a, ReassembleHList xs (CheckEnv a)) =>
241− HList xs ->
242− ProdCons [Traced a] ->
243− StructuralCompatFormula ()
234+structuralList
235+ :: (Subtree a, ReassembleHList xs (CheckEnv a))
236+ => HList xs
237+ -> ProdCons [Traced a]
238+ -> StructuralCompatFormula ()
244239structuralList _ (ProdCons [] []) = pure ()
245240structuralList e (ProdCons (a : aa) (b : bb)) = do
246241 checkSubstructure e $ ProdCons a b
@@ -253,11 +248,11 @@ structuralEq :: (Eq a, Comonad w) => ProdCons (w a) -> StructuralCompatFormula (
253248structuralEq (ProdCons a b) = if extract a == extract b then pure () else structuralIssue
254249{-# INLINE structuralEq #-}
255250
256−iohmStructural ::
257− (ReassembleHList (k ': xs) (CheckEnv v), Ord k, Subtree v, Hashable k, Typeable k, Show k) =>
258− HList xs ->
259− ProdCons (Traced (IOHM.InsOrdHashMap k v)) ->
260− StructuralCompatFormula ()
251+iohmStructural
252+ :: (ReassembleHList (k ': xs) (CheckEnv v), Ord k, Subtree v, Hashable k, Typeable k, Show k)
253+ => HList xs
254+ -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
255+ -> StructuralCompatFormula ()
261256iohmStructural e =
262257 iohmStructuralWith (\k -> checkSubstructure (k `HCons` e))
263258{-# INLINE iohmStructural #-}
@@ -266,26 +261,25 @@ instance (Typeable k, Typeable v, Ord k, Show k) => Steppable (IOHM.InsOrdHashMa
266261 data Step (IOHM.InsOrdHashMap k v) v = InsOrdHashMapKeyStep k
267262 deriving stock (Eq, Ord, Show)
268263
269−iohmStructuralWith ::
270− (Ord k, Hashable k, Typeable k, Typeable v, Show k) =>
271− (k -> ProdCons (Traced v) -> StructuralCompatFormula ()) ->
272− ProdCons (Traced (IOHM.InsOrdHashMap k v)) ->
273− StructuralCompatFormula ()
264+iohmStructuralWith
265+ :: (Ord k, Hashable k, Typeable k, Typeable v, Show k)
266+ => (k -> ProdCons (Traced v) -> StructuralCompatFormula ())
267+ -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
268+ -> StructuralCompatFormula ()
274269iohmStructuralWith f pc = do
275270 let ProdCons pEKeys cEKeys = S.fromList . IOHM.keys . extract <$> pc
276271 if pEKeys == cEKeys
277272 then
278273 for_
279274 pEKeys
280− ( \eKey ->
281− f eKey $ stepTraced (InsOrdHashMapKeyStep eKey) . fmap (IOHM.lookupDefault (error "impossible") eKey) <$> pc
282− )
275+ (\eKey ->
276+ f eKey $ stepTraced (InsOrdHashMapKeyStep eKey) . fmap (IOHM.lookupDefault (error "impossible") eKey) <$> pc)
283277 else structuralIssue
284278{-# INLINE iohmStructuralWith #-}
285279
286−runCompatFormula ::
287− CompatFormula' q f r a ->
288− Either (P.PathsPrefixTree q f r) a
280+runCompatFormula
281+ :: CompatFormula' q f r a
282+ -> Either (P.PathsPrefixTree q f r) a
289283runCompatFormula (Compose f) =
290284 calculate . runIdentity . runMemo 0 . unCompatM $ f
291285{-# INLINE runCompatFormula #-}
@@ -315,27 +309,27 @@ invertIssueOrientationP = P.map (\(AnIssue ori i) -> AnIssue (toggleOrientation
315309structuralIssue :: StructuralCompatFormula a
316310structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
317311
318−anyOfAt ::
319− Issuable l =>
320− Paths q r l ->
321− Issue l ->
322− [CompatFormula' q AnIssue r a] ->
323− CompatFormula' q AnIssue r a
312+anyOfAt
313+ :: Issuable l
314+ => Paths q r l
315+ -> Issue l
316+ -> [CompatFormula' q AnIssue r a]
317+ -> CompatFormula' q AnIssue r a
324318anyOfAt _ _ [x] = x
325319anyOfAt xs issue fs =
326320 Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> traverse getCompose fs
327321
328322-- | If the given formula contains any issues, add another issue on top. Otherwise succeed.
329−clarifyIssue ::
330− AnItem q AnIssue r ->
331− CompatFormula' q AnIssue r a ->
332− CompatFormula' q AnIssue r a
323+clarifyIssue
324+ :: AnItem q AnIssue r
325+ -> CompatFormula' q AnIssue r a
326+ -> CompatFormula' q AnIssue r a
333327clarifyIssue item f =
334328 Compose ((`eitherOf` item) <$> pure <$> getCompose f) *> f
335329
336−fixpointKnot ::
337− MonadState (MemoState VarRef) m =>
338− KnotTier (FormulaF q f r ()) VarRef m
330+fixpointKnot
331+ :: MonadState (MemoState VarRef) m
332+ => KnotTier (FormulaF q f r ()) VarRef m
339333fixpointKnot =
340334 KnotTier
341335 { onKnotFound = modifyMemoNonce succ
@@ -343,20 +337,19 @@ fixpointKnot =
343337 , tieKnot = \i x -> pure $ maxFixpoint i x
344338 }
345339
346−memo ::
347− (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a) =>
348− Paths q r l ->
349− MemoKey ->
350− (ProdCons (Traced a) -> CompatFormula' q f r ()) ->
351− (ProdCons (Traced a) -> CompatFormula' q f r ())
340+memo
341+ :: (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a)
342+ => Paths q r l
343+ -> MemoKey
344+ -> (ProdCons (Traced a) -> CompatFormula' q f r ())
345+ -> (ProdCons (Traced a) -> CompatFormula' q f r ())
352346memo bhv k f pc = Compose $ do
353347 formula' <-
354348 memoWithKnot
355349 fixpointKnot
356− ( do
357− formula <- getCompose $ f pc
358− pure $ mapErrors (P.takeSubtree bhv) formula
359− )
350+ (do
351+ formula <- getCompose $ f pc
352+ pure $ mapErrors (P.takeSubtree bhv) formula)
360353 (k, ask <$> pc)
361354 pure $ mapErrors (P.embed bhv) formula'
362355
src/Data/OpenApi/Compare/Validate/Header.hs → src/OpenAPI/Checker/Validate/Header.hsrenamed+7−9
@@ -1,6 +1,6 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.Header
3+module OpenAPI.Checker.Validate.Header
44 (
55 )
66where
@@ -9,10 +9,10 @@ import Data.Foldable
99import Data.Functor
1010import Data.Maybe
1111import Data.OpenApi
12−import Data.OpenApi.Compare.Behavior
13−import Data.OpenApi.Compare.References ()
14−import Data.OpenApi.Compare.Subtree
15−import Data.OpenApi.Compare.Validate.Schema ()
12+import OpenAPI.Checker.Behavior
13+import OpenAPI.Checker.References ()
14+import OpenAPI.Checker.Subtree
15+import OpenAPI.Checker.Validate.Schema ()
1616import Text.Pandoc.Builder
1717
1818instance Subtree Header where
@@ -50,9 +50,7 @@ instance Issuable 'HeaderLevel where
5050 | NonEmptyHeaderRequired
5151 | HeaderSchemaRequired
5252 deriving stock (Eq, Ord, Show)
53− issueKind = \case
54− HeaderSchemaRequired -> ProbablyIssue -- catch-all schema?
55− _ -> CertainIssue
53+ issueIsUnsupported _ = False
5654 describeIssue Forward RequiredHeaderMissing = para "Header has become required."
5755 describeIssue Backward RequiredHeaderMissing = para "Header is no longer required."
5856 describeIssue Forward NonEmptyHeaderRequired = para "The header does not allow empty values anymore."
@@ -63,4 +61,4 @@ instance Behavable 'HeaderLevel 'SchemaLevel where
6361 data Behave 'HeaderLevel 'SchemaLevel
6462 = InSchema
6563 deriving stock (Eq, Ord, Show)
66− describeBehavior InSchema = "JSON Schema"
64+ describeBehaviour InSchema = "JSON Schema"
src/Data/OpenApi/Compare/Validate/Link.hs → src/OpenAPI/Checker/Validate/Link.hsrenamed+6−6
@@ -1,10 +1,10 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.Link () where
3+module OpenAPI.Checker.Validate.Link () where
44
55import Data.OpenApi
6−import Data.OpenApi.Compare.Behavior
7−import Data.OpenApi.Compare.Subtree
6+import OpenAPI.Checker.Behavior
7+import OpenAPI.Checker.Subtree
88import Text.Pandoc.Builder
99
1010instance Subtree Link where
@@ -17,6 +17,6 @@ instance Issuable 'LinkLevel where
1717 data Issue 'LinkLevel
1818 = LinksUnsupported
1919 deriving stock (Eq, Ord, Show)
20− issueKind = \case
21− LinksUnsupported -> Unsupported
22− describeIssue _ LinksUnsupported = para "CompaREST does not currently support Link Objects."
20+ issueIsUnsupported = \case
21+ LinksUnsupported -> True
22+ describeIssue _ LinksUnsupported = para "OpenApi Diff does not currently support Link Objects."
src/Data/OpenApi/Compare/Validate/MediaTypeObject.hs → src/OpenAPI/Checker/Validate/MediaTypeObject.hsrenamed+17−17
@@ -1,8 +1,8 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.MediaTypeObject
4− ( Issue (..),
5− Behave (..),
3+module OpenAPI.Checker.Validate.MediaTypeObject
4+ ( Issue (..)
5+ , Behave (..)
66 )
77where
88
@@ -12,15 +12,15 @@ import Data.HList
1212import Data.HashMap.Strict.InsOrd as IOHM
1313import Data.Map.Strict as M
1414import Data.OpenApi
15−import Data.OpenApi.Compare.Behavior
16−import Data.OpenApi.Compare.Subtree
17−import Data.OpenApi.Compare.Validate.Header ()
18−import Data.OpenApi.Compare.Validate.Products
19−import Data.OpenApi.Compare.Validate.Schema ()
2015import Data.String
2116import Data.Text (Text)
2217import qualified Data.Text as T
2318import Network.HTTP.Media (MediaType, mainType, subType)
19+import OpenAPI.Checker.Behavior
20+import OpenAPI.Checker.Subtree
21+import OpenAPI.Checker.Validate.Header ()
22+import OpenAPI.Checker.Validate.Products
23+import OpenAPI.Checker.Validate.Schema ()
2424import Text.Pandoc.Builder
2525
2626tracedSchema :: Traced MediaTypeObject -> Maybe (Traced (Referenced Schema))
@@ -38,20 +38,20 @@ instance Issuable 'PayloadLevel where
3838 | MediaEncodingMissing Text
3939 | EncodingNotSupported
4040 deriving stock (Eq, Ord, Show)
41− issueKind = \case
42− EncodingNotSupported -> Unsupported
43− _ -> CertainIssue
41+ issueIsUnsupported = \case
42+ EncodingNotSupported -> True
43+ _ -> False
4444
4545 describeIssue _ MediaTypeSchemaRequired = para "Media type expected, but was not specified."
4646 describeIssue Forward (MediaEncodingMissing enc) = para $ "Media encoding " <> str enc <> " has been removed."
4747 describeIssue Backward (MediaEncodingMissing enc) = para $ "Media encoding " <> str enc <> " added."
48− describeIssue _ EncodingNotSupported = para "CompaREST does not currently support media encodings other than JSON."
48+ describeIssue _ EncodingNotSupported = para "OpenApi Diff does not currently support media encodings other than JSON."
4949
5050instance Behavable 'PayloadLevel 'SchemaLevel where
5151 data Behave 'PayloadLevel 'SchemaLevel
5252 = PayloadSchema
5353 deriving stock (Eq, Ord, Show)
54− describeBehavior PayloadSchema = "JSON Schema"
54+ describeBehaviour PayloadSchema = "JSON Schema"
5555
5656instance Subtree MediaTypeObject where
5757 type SubtreeLevel MediaTypeObject = 'PayloadLevel
@@ -119,7 +119,7 @@ instance Subtree Encoding where
119119
120120 -- FIXME: Support only JSON body for now. Then Encoding is checked only for
121121 -- multipart/form-url-encoded
122− -- #54
122+ -- https://github.com/typeable/openapi-diff/issues/54
123123 checkSemanticCompatibility _env beh _pc =
124124 issueAt beh EncodingNotSupported
125125
@@ -143,7 +143,7 @@ instance Behavable 'OperationLevel 'ResponseLevel where
143143 data Behave 'OperationLevel 'ResponseLevel
144144 = WithStatusCode HttpStatusCode
145145 deriving stock (Eq, Ord, Show)
146− describeBehavior (WithStatusCode c) = "Response code " <> (fromString . show $ c)
146+ describeBehaviour (WithStatusCode c) = "Response code " <> (fromString . show $ c)
147147
148148instance Issuable 'OperationLevel where
149149 data Issue 'OperationLevel
@@ -152,8 +152,8 @@ instance Issuable 'OperationLevel where
152152 | PathFragmentNotMatched Int
153153 | NoRequestBody
154154 deriving stock (Eq, Ord, Show)
155− issueKind = \case
156− _ -> CertainIssue
155+ issueIsUnsupported = \case
156+ _ -> False
157157 describeIssue Forward (ConsumerDoesntHaveResponseCode c) =
158158 para $ "Response code " <> (str . T.pack . show $ c) <> " has been removed."
159159 describeIssue Backward (ConsumerDoesntHaveResponseCode c) =
src/Data/OpenApi/Compare/Validate/OAuth2Flows.hs → src/OpenAPI/Checker/Validate/OAuth2Flows.hsrenamed+21−26
@@ -1,9 +1,9 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.OAuth2Flows
4− ( Step (..),
5− Issue (..),
6− Behave (..),
3+module OpenAPI.Checker.Validate.OAuth2Flows
4+ ( Step (..)
5+ , Issue (..)
6+ , Behave (..)
77 )
88where
99
@@ -12,13 +12,13 @@ import Data.Function
1212import Data.Functor
1313import qualified Data.HashMap.Strict.InsOrd as IOHM
1414import Data.OpenApi
15−import Data.OpenApi.Compare.Behavior
16−import Data.OpenApi.Compare.Orphans ()
17−import Data.OpenApi.Compare.Subtree
1815import Data.Proxy
1916import Data.Set (Set)
2017import qualified Data.Set as S
2118import Data.Text (Text)
19+import OpenAPI.Checker.Behavior
20+import OpenAPI.Checker.Orphans ()
21+import OpenAPI.Checker.Subtree
2222import Text.Pandoc.Builder
2323
2424instance Subtree OAuth2Flows where
@@ -26,11 +26,11 @@ instance Subtree OAuth2Flows where
2626 type SubtreeLevel OAuth2Flows = 'SecuritySchemeLevel
2727 checkStructuralCompatibility _ = structuralEq
2828 checkSemanticCompatibility env bhv pc = do
29− let supportFlow ::
30− (Subtree t, SubtreeLevel t ~ SubtreeLevel OAuth2Flows, CheckEnv OAuth2Flows ~ CheckEnv t) =>
31− Issue 'SecuritySchemeLevel ->
32− ProdCons (Maybe (Traced t)) ->
33− SemanticCompatFormula ()
29+ let supportFlow
30+ :: (Subtree t, SubtreeLevel t ~ SubtreeLevel OAuth2Flows, CheckEnv OAuth2Flows ~ CheckEnv t)
31+ => Issue 'SecuritySchemeLevel
32+ -> ProdCons (Maybe (Traced t))
33+ -> SemanticCompatFormula ()
3434 supportFlow i x = case x of
3535 -- producer will not attempt this flow
3636 (ProdCons Nothing _) -> pure ()
@@ -38,11 +38,11 @@ instance Subtree OAuth2Flows where
3838 (ProdCons (Just _) Nothing) -> issueAt bhv i
3939 (ProdCons (Just p) (Just c)) ->
4040 checkCompatibility bhv env $ ProdCons p c
41− getFlow ::
42− Typeable x =>
43− (OAuth2Flows -> Maybe (OAuth2Flow x)) ->
44− Traced OAuth2Flows ->
45− Maybe (Traced (OAuth2Flow x))
41+ getFlow
42+ :: Typeable x
43+ => (OAuth2Flows -> Maybe (OAuth2Flow x))
44+ -> Traced OAuth2Flows
45+ -> Maybe (Traced (OAuth2Flow x))
4646 getFlow f (Traced t a) = Traced (t >>> step (OAuth2FlowsFlow Proxy)) <$> f a
4747 supportFlow ConsumerDoesNotSupportImplicitFlow $ getFlow _oAuth2FlowsImplicit <$> pc
4848 supportFlow ConsumerDoesNotSupportPasswordFlow $ getFlow _oAuth2FlowsPassword <$> pc
@@ -103,9 +103,7 @@ instance Issuable 'SecurityRequirementLevel where
103103 = SecurityRequirementNotMet
104104 | UndefinedSecurityScheme Text
105105 deriving stock (Eq, Ord, Show)
106− issueKind = \case
107− SecurityRequirementNotMet -> CertainIssue
108− UndefinedSecurityScheme _ -> SchemaInvalid
106+ issueIsUnsupported _ = False
109107 describeIssue Forward SecurityRequirementNotMet = para "Security scheme has been removed."
110108 describeIssue Backward SecurityRequirementNotMet = para "Security scheme was added."
111109 describeIssue _ (UndefinedSecurityScheme k) = para $ "Security scheme " <> code k <> " is not defined."
@@ -131,14 +129,11 @@ instance Issuable 'SecuritySchemeLevel where
131129 | CanNotHaveScopes
132130 | ScopeNotDefined Text
133131 deriving stock (Eq, Ord, Show)
134− issueKind = \case
135− CanNotHaveScopes -> SchemaInvalid
136− ScopeNotDefined _ -> SchemaInvalid
137− _ -> CertainIssue
132+ issueIsUnsupported _ = False
138133 describeIssue _ RefreshUrlsDontMatch = para "Refresh URL changed."
139134 describeIssue _ (HttpSchemeTypesDontMatch _ _) = para "HTTP scheme type changed."
140135 describeIssue _ (ApiKeyParamsDontMatch _ _) = para "API Key parameters changed."
141− describeIssue _ (OpenIdConnectUrlsDontMatch _ _) = para "OpenID Connect URL changed."
136+ describeIssue _ (OpenIdConnectUrlsDontMatch _ _) = para "OpenaId Connect URL changed."
142137 describeIssue _ (CustomHttpSchemesDontMatch e a) =
143138 para $ "Changed HTTP scheme from " <> code e <> " to " <> code a <> "."
144139 describeIssue Forward ConsumerDoesNotSupportImplicitFlow = para "Implicit flow support has been removed."
@@ -167,4 +162,4 @@ instance Behavable 'SecurityRequirementLevel 'SecuritySchemeLevel where
167162 data Behave 'SecurityRequirementLevel 'SecuritySchemeLevel
168163 = SecuritySchemeStep Text
169164 deriving stock (Eq, Ord, Show)
170− describeBehavior (SecuritySchemeStep s) = text s
165+ describeBehaviour (SecuritySchemeStep s) = text s
src/Data/OpenApi/Compare/Validate/OpenApi.hs → src/OpenAPI/Checker/Validate/OpenApi.hsrenamed+18−20
@@ -2,19 +2,19 @@
22-- Does not compiles otherwise
33{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
44
5−module Data.OpenApi.Compare.Validate.OpenApi
6− ( Behave (..),
7− Issue (..),
5+module OpenAPI.Checker.Validate.OpenApi
6+ ( Behave (..)
7+ , Issue (..)
88 )
99where
1010
1111import Data.HList
1212import qualified Data.HashMap.Strict.InsOrd as IOHM
1313import Data.OpenApi
14−import Data.OpenApi.Compare.Behavior
15−import Data.OpenApi.Compare.Paths
16−import Data.OpenApi.Compare.Subtree
17−import Data.OpenApi.Compare.Validate.Operation
14+import OpenAPI.Checker.Behavior
15+import OpenAPI.Checker.Paths
16+import OpenAPI.Checker.Subtree
17+import OpenAPI.Checker.Validate.Operation
1818
1919tracedPaths :: Traced OpenApi -> Traced ProcessedPathItems
2020tracedPaths oa =
@@ -38,8 +38,7 @@ tracedSecuritySchemes :: Traced OpenApi -> Traced (Definitions SecurityScheme)
3838tracedSecuritySchemes oa =
3939 traced
4040 (ask oa >>> step ComponentsSecurityScheme)
41− (unSecurityDefinitions . _componentsSecuritySchemes . _openApiComponents . extract $ oa)
42− where unSecurityDefinitions (SecurityDefinitions ds) = ds
41+ (_componentsSecuritySchemes . _openApiComponents . extract $ oa)
4342
4443tracedResponses :: Traced OpenApi -> Traced (Definitions Response)
4544tracedResponses oa =
@@ -80,17 +79,16 @@ instance Subtree OpenApi where
8079 checkSemanticCompatibility _ beh prodCons = do
8180 checkCompatibility @ProcessedPathItems
8281 beh
83− ( (tracedRequestBodies <$> prodCons)
84− `HCons` (tracedParameters <$> prodCons)
85− `HCons` (tracedSecuritySchemes <$> prodCons)
86− `HCons` (tracedResponses <$> prodCons)
87− `HCons` (tracedHeaders <$> prodCons)
88− `HCons` (tracedSchemas <$> prodCons)
89− `HCons` (_openApiServers . extract <$> prodCons)
90− `HCons` (tracedLinks <$> prodCons)
91− `HCons` (tracedCallbacks <$> prodCons)
92− `HCons` HNil
93− )
82+ ((tracedRequestBodies <$> prodCons)
83+ `HCons` (tracedParameters <$> prodCons)
84+ `HCons` (tracedSecuritySchemes <$> prodCons)
85+ `HCons` (tracedResponses <$> prodCons)
86+ `HCons` (tracedHeaders <$> prodCons)
87+ `HCons` (tracedSchemas <$> prodCons)
88+ `HCons` (_openApiServers . extract <$> prodCons)
89+ `HCons` (tracedLinks <$> prodCons)
90+ `HCons` (tracedCallbacks <$> prodCons)
91+ `HCons` HNil)
9492 (tracedPaths <$> prodCons)
9593
9694instance Steppable OpenApi ProcessedPathItems where
src/Data/OpenApi/Compare/Validate/Operation.hs → src/OpenAPI/Checker/Validate/Operation.hsrenamed+50−57
@@ -2,19 +2,19 @@
22{-# OPTIONS_GHC -Wno-orphans #-}
33{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
44
5−module Data.OpenApi.Compare.Validate.Operation
5+module OpenAPI.Checker.Validate.Operation
66 ( -- * Operation
7− MatchedOperation (..),
8− OperationMethod (..),
9− pathItemMethod,
7+ MatchedOperation (..)
8+ , OperationMethod (..)
9+ , pathItemMethod
1010
1111 -- * ProcessedPathItem
12− ProcessedPathItem (..),
13− ProcessedPathItems (..),
14− processPathItems,
15− Step (..),
16− Behave (..),
17− Issue (..),
12+ , ProcessedPathItem (..)
13+ , ProcessedPathItems (..)
14+ , processPathItems
15+ , Step (..)
16+ , Behave (..)
17+ , Issue (..)
1818 )
1919where
2020
@@ -29,21 +29,21 @@ import qualified Data.List as L
2929import Data.Map.Strict as M
3030import Data.Maybe
3131import Data.OpenApi
32−import Data.OpenApi.Compare.Behavior
33−import Data.OpenApi.Compare.References
34−import Data.OpenApi.Compare.Subtree
35−import Data.OpenApi.Compare.Validate.MediaTypeObject
36−import Data.OpenApi.Compare.Validate.OAuth2Flows
37−import Data.OpenApi.Compare.Validate.PathFragment
38−import Data.OpenApi.Compare.Validate.Products
39−import Data.OpenApi.Compare.Validate.RequestBody
40−import Data.OpenApi.Compare.Validate.Responses
41−import Data.OpenApi.Compare.Validate.SecurityRequirement ()
42−import Data.OpenApi.Compare.Validate.Server ()
43−import Data.OpenApi.Compare.Validate.Sums
4432import Data.String
4533import Data.Text (Text)
4634import qualified Data.Text as T
35+import OpenAPI.Checker.Behavior
36+import OpenAPI.Checker.References
37+import OpenAPI.Checker.Subtree
38+import OpenAPI.Checker.Validate.MediaTypeObject
39+import OpenAPI.Checker.Validate.OAuth2Flows
40+import OpenAPI.Checker.Validate.PathFragment
41+import OpenAPI.Checker.Validate.Products
42+import OpenAPI.Checker.Validate.RequestBody
43+import OpenAPI.Checker.Validate.Responses
44+import OpenAPI.Checker.Validate.SecurityRequirement ()
45+import OpenAPI.Checker.Validate.Server ()
46+import OpenAPI.Checker.Validate.Sums
4747import Text.Pandoc.Builder
4848
4949data MatchedOperation = MatchedOperation
@@ -86,12 +86,11 @@ tracedCallbacks (Traced t oper) =
8686 | (k, v) <- IOHM.toList . _operationCallbacks . operation $ oper
8787 ]
8888
89−-- FIXME: #28
90−getServers ::
91− -- | Servers from env
92− [Server] ->
93− MatchedOperation ->
94− [Server]
89+-- FIXME: https://github.com/typeable/openapi-diff/issues/28
90+getServers
91+ :: [Server] -- ^ Servers from env
92+ -> MatchedOperation
93+ -> [Server]
9594getServers env oper =
9695 case _operationServers . operation $ oper of
9796 [] -> env
@@ -102,21 +101,21 @@ instance Behavable 'OperationLevel 'PathFragmentLevel where
102101 = InParam Text
103102 | InFragment (PathFragment Text)
104103 deriving stock (Eq, Ord, Show)
105− describeBehavior (InParam p) = "Parameter " <> text p
106− describeBehavior (InFragment (StaticPath p)) = "Static fragment " <> code p
107− describeBehavior (InFragment (DynamicPath p)) = "Dynamic fragment " <> code p
104+ describeBehaviour (InParam p) = "Parameter " <> text p
105+ describeBehaviour (InFragment (StaticPath p)) = "Static fragment " <> code p
106+ describeBehaviour (InFragment (DynamicPath p)) = "Dynamic fragment " <> code p
108107
109108instance Behavable 'OperationLevel 'RequestLevel where
110109 data Behave 'OperationLevel 'RequestLevel
111110 = InRequest
112111 deriving stock (Eq, Ord, Show)
113− describeBehavior InRequest = "Request"
112+ describeBehaviour InRequest = "Request"
114113
115114instance Behavable 'OperationLevel 'SecurityRequirementLevel where
116115 data Behave 'OperationLevel 'SecurityRequirementLevel
117116 = SecurityRequirementStep Int
118117 deriving stock (Eq, Ord, Show)
119− describeBehavior (SecurityRequirementStep i) =
118+ describeBehaviour (SecurityRequirementStep i) =
120119 "Security requirement " <> (text . T.pack . show $ i)
121120
122121instance Subtree MatchedOperation where
@@ -242,7 +241,7 @@ instance Subtree MatchedOperation where
242241 checkProducts beh (const NoRequestBody) (const check) elements
243242 checkResponses =
244243 swapProdCons (checkCompatibility beh) env $ tracedResponses <$> prodCons
245− -- FIXME: #27
244+ -- FIXME: https://github.com/typeable/openapi-diff/issues/27
246245 checkCallbacks = do
247246 let ProdCons pCallbacks cCallbacks = tracedCallbacks <$> prodCons
248247 for_ pCallbacks $ \(k, pCallback) -> do
@@ -251,7 +250,7 @@ instance Subtree MatchedOperation where
251250 cCallbacks <&> \(_, cCallback) -> do
252251 swapProdCons (checkCompatibility beh') env $ ProdCons pCallback cCallback
253252 pure ()
254− -- FIXME: #28
253+ -- FIXME: https://github.com/typeable/openapi-diff/issues/28
255254 checkOperationSecurity = do
256255 let ProdCons pSecs cSecs = tracedSecurity <$> prodCons
257256 for_ pSecs $ \(i, pSec) -> do
@@ -336,12 +335,7 @@ instance Issuable 'APILevel where
336335 | AllPathsFailed FilePath
337336 -- When several paths match given but all checks failed
338337 deriving stock (Eq, Ord, Show)
339− issueKind = \case
340− _ -> CertainIssue
341− relatedIssues =
342− (==) `withClass` \case
343− NoPathsMatched fp -> Just fp
344− AllPathsFailed fp -> Just fp
338+ issueIsUnsupported _ = False
345339 describeIssue Forward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
346340 describeIssue Backward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been added."
347341 describeIssue Forward (AllPathsFailed p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
@@ -352,7 +346,7 @@ instance Behavable 'APILevel 'PathLevel where
352346 = AtPath FilePath
353347 deriving stock (Eq, Ord, Show)
354348
355− describeBehavior (AtPath p) = str (T.pack p)
349+ describeBehaviour (AtPath p) = str (T.pack p)
356350
357351instance Subtree ProcessedPathItems where
358352 type SubtreeLevel ProcessedPathItems = 'APILevel
@@ -436,18 +430,17 @@ tracedFragments mpi =
436430 | (i, x) <- L.zip [0 ..] $ pathFragments $ extract mpi
437431 ]
438432
439−tracedMethod ::
440− OperationMethod ->
441− Traced MatchedPathItem ->
442− Maybe (Traced' MatchedOperation Operation)
433+tracedMethod
434+ :: OperationMethod
435+ -> Traced MatchedPathItem
436+ -> Maybe (Traced' MatchedOperation Operation)
443437tracedMethod s mpi = env (ask mpi >>> step (OperationMethodStep s)) <$> (pathItemMethod s . pathItem . extract $ mpi)
444438
445439instance Issuable 'PathLevel where
446440 data Issue 'PathLevel
447441 = OperationMissing OperationMethod
448442 deriving stock (Eq, Ord, Show)
449− issueKind = \case
450− OperationMissing _ -> CertainIssue
443+ issueIsUnsupported _ = False
451444 describeIssue Forward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been removed."
452445 describeIssue Backward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been added."
453446
@@ -456,7 +449,7 @@ instance Behavable 'PathLevel 'OperationLevel where
456449 = InOperation OperationMethod
457450 deriving stock (Eq, Ord, Show)
458451
459− describeBehavior (InOperation method) = showMethod method
452+ describeBehaviour (InOperation method) = showMethod method
460453
461454showMethod :: IsString s => OperationMethod -> s
462455showMethod = \case
@@ -487,10 +480,10 @@ instance Subtree MatchedPathItem where
487480 checkSemanticCompatibility env beh prodCons = do
488481 let paramDefs = getH @(ProdCons (Traced (Definitions Param))) env
489482 pathTracedParams = getPathParams <$> paramDefs <*> prodCons
490− getPathParams ::
491− Traced (Definitions Param) ->
492− Traced MatchedPathItem ->
493− [Traced Param]
483+ getPathParams
484+ :: Traced (Definitions Param)
485+ -> Traced MatchedPathItem
486+ -> [Traced Param]
494487 getPathParams defs mpi = do
495488 paramRef <- tracedMatchedPathItemParameters mpi
496489 pure $ dereference defs paramRef
@@ -564,9 +557,9 @@ instance Issuable 'CallbackLevel where
564557 data Issue 'CallbackLevel
565558 = CallbacksUnsupported
566559 deriving stock (Eq, Ord, Show)
567− issueKind = \case
568− CallbacksUnsupported -> Unsupported
569− describeIssue _ CallbacksUnsupported = para "CompaREST does not currently support callbacks."
560+ issueIsUnsupported = \case
561+ CallbacksUnsupported -> True
562+ describeIssue _ CallbacksUnsupported = para "OpenApi Diff does not currently support callbacks."
570563
571564tracedCallbackPathItems :: Traced Callback -> Traced ProcessedPathItems
572565tracedCallbackPathItems (Traced t (Callback x)) =
@@ -580,4 +573,4 @@ instance Behavable 'OperationLevel 'CallbackLevel where
580573 data Behave 'OperationLevel 'CallbackLevel = OperationCallback Text
581574 deriving stock (Eq, Ord, Show)
582575
583− describeBehavior (OperationCallback key) = "Operation " <> code key
576+ describeBehaviour (OperationCallback key) = "Operation " <> code key
src/Data/OpenApi/Compare/Validate/Param.hs → src/OpenAPI/Checker/Validate/Param.hsrenamed+15−20
@@ -1,9 +1,9 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
33
4−module Data.OpenApi.Compare.Validate.Param
5− ( Behave (..),
6− Issue (..),
4+module OpenAPI.Checker.Validate.Param
5+ ( Behave (..)
6+ , Issue (..)
77 )
88where
99
@@ -11,11 +11,11 @@ import Control.Monad
1111import Data.Functor
1212import Data.Maybe
1313import Data.OpenApi
14−import Data.OpenApi.Compare.Behavior
15−import Data.OpenApi.Compare.Orphans ()
16−import Data.OpenApi.Compare.Subtree
17−import Data.OpenApi.Compare.Validate.Schema ()
1814import Data.Text as T
15+import OpenAPI.Checker.Behavior
16+import OpenAPI.Checker.Orphans ()
17+import OpenAPI.Checker.Subtree
18+import OpenAPI.Checker.Validate.Schema ()
1919import Text.Pandoc.Builder
2020
2121-- | The type is normalized encoding style of the parameter. If two encoding
@@ -66,11 +66,9 @@ instance Issuable 'PathFragmentLevel where
6666 ParamStyleMismatch
6767 | -- | One of schemas not presented
6868 ParamSchemaMismatch
69− | PathFragmentsDontMatch (ProdCons Text)
69+ | PathFragmentsDontMatch Text Text
7070 deriving stock (Eq, Ord, Show)
71− issueKind = \case
72− ParamSchemaMismatch -> ProbablyIssue -- the schema could be catch-all (?)
73− _ -> CertainIssue
71+ issueIsUnsupported _ = False
7472 describeIssue _ ParamNameMismatch = para "The path fragments don't match."
7573 describeIssue Forward ParamEmptinessIncompatible = para "The parameter can no longer be empty."
7674 describeIssue Backward ParamEmptinessIncompatible = para "The parameter can now be empty."
@@ -79,15 +77,14 @@ instance Issuable 'PathFragmentLevel where
7977 describeIssue _ ParamPlaceIncompatible = para "Parameters in incompatible locations."
8078 describeIssue _ ParamStyleMismatch = para "Different parameter styles (encodings)."
8179 describeIssue _ ParamSchemaMismatch = para "Expected a schema, but didn't find one."
82− describeIssue ori (PathFragmentsDontMatch (orientProdCons ori -> ProdCons e a)) =
83− para $ "Parameter changed from " <> code e <> " to " <> code a <> "."
80+ describeIssue _ (PathFragmentsDontMatch e a) = para $ "Parameter changed from " <> code e <> " to " <> code a <> "."
8481
8582instance Behavable 'PathFragmentLevel 'SchemaLevel where
8683 data Behave 'PathFragmentLevel 'SchemaLevel
8784 = InParamSchema
8885 deriving stock (Eq, Ord, Show)
8986
90− describeBehavior InParamSchema = "JSON Schema"
87+ describeBehaviour InParamSchema = "JSON Schema"
9188
9289instance Subtree Param where
9390 type SubtreeLevel Param = 'PathFragmentLevel
@@ -106,17 +103,15 @@ instance Subtree Param where
106103 when (_paramName (extract p) /= _paramName (extract c)) $
107104 issueAt beh ParamNameMismatch
108105 when
109− ( (fromMaybe False . _paramRequired . extract $ c)
110− && not (fromMaybe False . _paramRequired . extract $ p)
111− )
106+ ((fromMaybe False . _paramRequired . extract $ c)
107+ && not (fromMaybe False . _paramRequired . extract $ p))
112108 $ issueAt beh ParamRequired
113109 case (_paramIn . extract $ p, _paramIn . extract $ c) of
114110 (ParamQuery, ParamQuery) -> do
115111 -- Emptiness is only for query params
116112 when
117− ( (fromMaybe False . _paramAllowEmptyValue . extract $ p)
118− && not (fromMaybe False . _paramAllowEmptyValue . extract $ c)
119− )
113+ ((fromMaybe False . _paramAllowEmptyValue . extract $ p)
114+ && not (fromMaybe False . _paramAllowEmptyValue . extract $ c))
120115 $ issueAt beh ParamEmptinessIncompatible
121116 (a, b) | a == b -> pure ()
122117 _ -> issueAt beh ParamPlaceIncompatible
src/Data/OpenApi/Compare/Validate/PathFragment.hs → src/OpenAPI/Checker/Validate/PathFragment.hsrenamed+10−9
@@ -1,20 +1,21 @@
1−module Data.OpenApi.Compare.Validate.PathFragment
2− ( parsePath,
3− PathFragment (..),
4− PathFragmentParam,
1+module OpenAPI.Checker.Validate.PathFragment
2+ ( parsePath
3+ , PathFragment (..)
4+ , PathFragmentParam
55 )
66where
77
88import qualified Data.Aeson as A
99import Data.OpenApi
10−import Data.OpenApi.Compare.Behavior
11−import Data.OpenApi.Compare.Subtree
12−import Data.OpenApi.Compare.Validate.Param
1310import Data.Text (Text)
1411import qualified Data.Text as T
12+import Data.Typeable
13+import OpenAPI.Checker.Behavior
14+import OpenAPI.Checker.Subtree
15+import OpenAPI.Checker.Validate.Param
1516
1617-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
17−-- #23
18+-- https://github.com/typeable/openapi-diff/issues/23
1819parsePath :: FilePath -> [PathFragment Text]
1920parsePath = fmap partition . T.splitOn "/" . T.pack
2021 where
@@ -73,6 +74,6 @@ instance Subtree PathFragmentParam where
7374 checkSemanticCompatibility _ beh (ProdCons (extract -> StaticPath x) (extract -> StaticPath y)) =
7475 if x == y
7576 then pure ()
76− else issueAt beh (PathFragmentsDontMatch (ProdCons x y))
77+ else issueAt beh (PathFragmentsDontMatch x y)
7778 checkSemanticCompatibility env beh prodCons = do
7879 checkCompatibility beh env (tracedPathFragmentParam <$> prodCons)
src/OpenAPI/Checker/Validate/Products.hsnew file+49
@@ -0,0 +1,49 @@
1+{- | Checks product-like entities. The key is some identificator for the product
2+element. Each element may be required or optional.
3+
4+One example of product is request parameters. There are optional and required
5+parameters. The client and server have possibly different set of
6+parameters. What we must check is if server requires some request parameter,
7+then this parameter must be presented by client and their schemas must match.
8+
9+So when we checking products we are checking from the server's (consumer)
10+perspective, ensuring that all parameters are provided by the client (producer)
11+and their schemas match.
12+
13+This module abstracts this logic for arbitrary elements -}
14+
15+module OpenAPI.Checker.Validate.Products
16+ ( checkProducts
17+ , ProductLike(..)
18+ ) where
19+
20+import Data.Foldable
21+import Data.Map.Strict (Map)
22+import qualified Data.Map.Strict as M
23+import OpenAPI.Checker.Behavior
24+import OpenAPI.Checker.Paths
25+import OpenAPI.Checker.Subtree
26+
27+-- | Some entity which is product-like
28+data ProductLike a = ProductLike
29+ { productValue :: a
30+ , required :: Bool
31+ }
32+
33+checkProducts
34+ :: (Ord k, Issuable l)
35+ => Paths q r l
36+ -> (k -> Issue l)
37+ -- ^ No required element found
38+ -> (k -> ProdCons t -> CompatFormula' q AnIssue r ())
39+ -> ProdCons (Map k (ProductLike t))
40+ -> CompatFormula' q AnIssue r ()
41+checkProducts xs noElt check (ProdCons p c) = for_ (M.toList c) $ \(key, consElt) ->
42+ case M.lookup key p of
43+ Nothing -> case required consElt of
44+ True -> issueAt xs $ noElt key
45+ False -> pure ()
46+ Just prodElt -> do
47+ let
48+ elts = ProdCons prodElt consElt
49+ check key (productValue <$> elts)
src/Data/OpenApi/Compare/Validate/RequestBody.hs → src/OpenAPI/Checker/Validate/RequestBody.hsrenamed+9−10
@@ -1,8 +1,8 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.RequestBody
4− ( Issue (..),
5− Behave (..),
3+module OpenAPI.Checker.Validate.RequestBody
4+ ( Issue (..)
5+ , Behave (..)
66 )
77where
88
@@ -11,12 +11,12 @@ import Data.HashMap.Strict.InsOrd as IOHM
1111import Data.Map.Strict as M
1212import Data.Maybe
1313import Data.OpenApi
14−import Data.OpenApi.Compare.Behavior
15−import Data.OpenApi.Compare.Subtree
16−import Data.OpenApi.Compare.Validate.MediaTypeObject
17−import Data.OpenApi.Compare.Validate.Sums
1814import qualified Data.Text as T
1915import Network.HTTP.Media (MediaType)
16+import OpenAPI.Checker.Behavior
17+import OpenAPI.Checker.Subtree
18+import OpenAPI.Checker.Validate.MediaTypeObject
19+import OpenAPI.Checker.Validate.Sums
2020import Text.Pandoc.Builder
2121
2222-- TODO: Use RequestMediaTypeObjectMapping
@@ -30,8 +30,7 @@ instance Issuable 'RequestLevel where
3030 = RequestBodyRequired
3131 | RequestMediaTypeNotFound MediaType
3232 deriving stock (Eq, Ord, Show)
33− issueKind = \case
34− _ -> CertainIssue
33+ issueIsUnsupported _ = False
3534 describeIssue Forward RequestBodyRequired =
3635 para "Request body has become required."
3736 describeIssue Backward RequestBodyRequired =
@@ -45,7 +44,7 @@ instance Behavable 'RequestLevel 'PayloadLevel where
4544 data Behave 'RequestLevel 'PayloadLevel
4645 = InPayload
4746 deriving stock (Eq, Ord, Show)
48− describeBehavior InPayload = "Payload"
47+ describeBehaviour InPayload = "Payload"
4948
5049instance Subtree RequestBody where
5150 type SubtreeLevel RequestBody = 'RequestLevel
src/Data/OpenApi/Compare/Validate/Responses.hs → src/OpenAPI/Checker/Validate/Responses.hsrenamed+14−15
@@ -1,8 +1,8 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
33
4−module Data.OpenApi.Compare.Validate.Responses
5− ( Behave (..),
4+module OpenAPI.Checker.Validate.Responses
5+ ( Behave (..)
66 )
77where
88
@@ -11,17 +11,17 @@ import Data.HashMap.Strict.InsOrd as IOHM
1111import Data.Map.Strict as M
1212import Data.Maybe
1313import Data.OpenApi
14−import Data.OpenApi.Compare.Behavior
15−import Data.OpenApi.Compare.References
16−import Data.OpenApi.Compare.Subtree
17−import Data.OpenApi.Compare.Validate.Header ()
18−import Data.OpenApi.Compare.Validate.Link ()
19−import Data.OpenApi.Compare.Validate.MediaTypeObject
20−import Data.OpenApi.Compare.Validate.Products
21−import Data.OpenApi.Compare.Validate.Schema ()
22−import Data.OpenApi.Compare.Validate.Sums
2314import qualified Data.Text as T
2415import Network.HTTP.Media (MediaType)
16+import OpenAPI.Checker.Behavior
17+import OpenAPI.Checker.References
18+import OpenAPI.Checker.Subtree
19+import OpenAPI.Checker.Validate.Header ()
20+import OpenAPI.Checker.Validate.Link ()
21+import OpenAPI.Checker.Validate.MediaTypeObject
22+import OpenAPI.Checker.Validate.Products
23+import OpenAPI.Checker.Validate.Schema ()
24+import OpenAPI.Checker.Validate.Sums
2525import Text.Pandoc.Builder
2626
2727tracedResponses :: Traced Responses -> IOHM.InsOrdHashMap HttpStatusCode (Traced (Referenced Response))
@@ -71,8 +71,7 @@ instance Issuable 'ResponseLevel where
7171 = ConsumerDoesntHaveMediaType MediaType
7272 | ProducerDoesntHaveResponseHeader HeaderName
7373 deriving stock (Eq, Ord, Show)
74− issueKind = \case
75− _ -> CertainIssue
74+ issueIsUnsupported _ = False
7675 describeIssue Forward (ConsumerDoesntHaveMediaType t) =
7776 para $ "Media type was removed: " <> (code . T.pack . show $ t) <> "."
7877 describeIssue Backward (ConsumerDoesntHaveMediaType t) =
@@ -87,14 +86,14 @@ instance Behavable 'ResponseLevel 'PayloadLevel where
8786 = ResponsePayload
8887 deriving stock (Eq, Ord, Show)
8988
90− describeBehavior ResponsePayload = "Payload"
89+ describeBehaviour ResponsePayload = "Payload"
9190
9291instance Behavable 'ResponseLevel 'HeaderLevel where
9392 data Behave 'ResponseLevel 'HeaderLevel
9493 = InHeader HeaderName
9594 deriving stock (Eq, Ord, Show)
9695
97− describeBehavior (InHeader name) = "Header " <> code name
96+ describeBehaviour (InHeader name) = "Header " <> code name
9897
9998instance Subtree Response where
10099 type SubtreeLevel Response = 'ResponseLevel
src/Data/OpenApi/Compare/Validate/Schema.hs → src/OpenAPI/Checker/Validate/Schema.hsrenamed+116−96
@@ -1,74 +1,72 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
2−
3−module Data.OpenApi.Compare.Validate.Schema
2+module OpenAPI.Checker.Validate.Schema
43 (
54 )
65where
76
8−import Control.Monad (when,unless)
7+import Control.Monad.Writer
98import qualified Data.Aeson as A
109import Data.Coerce
1110import Data.Foldable (for_, toList)
1211import Data.Functor
1312import Data.HList
14−import Data.List (genericIndex, genericLength, group)
1513import qualified Data.List.NonEmpty as NE
1614import qualified Data.Map as M
15+import Data.List (group, genericLength, genericIndex)
1716import Data.Maybe
1817import Data.OpenApi
19−import Data.OpenApi.Compare.Behavior
20−import Data.OpenApi.Compare.Paths
21−import qualified Data.OpenApi.Compare.PathsPrefixTree as P
22−import Data.OpenApi.Compare.Subtree
23−import Data.OpenApi.Compare.Validate.Schema.DNF
24−import Data.OpenApi.Compare.Validate.Schema.Issues
25−import Data.OpenApi.Compare.Validate.Schema.JsonFormula
26−import Data.OpenApi.Compare.Validate.Schema.Partition
27−import Data.OpenApi.Compare.Validate.Schema.Process
28−import Data.OpenApi.Compare.Validate.Schema.Traced
29−import Data.OpenApi.Compare.Validate.Schema.TypedJson
3018import Data.Ord
3119import Data.Ratio
3220import Data.Semigroup
3321import qualified Data.Set as S
3422import Data.Text (Text)
23+import OpenAPI.Checker.Behavior
24+import OpenAPI.Checker.Paths
25+import qualified OpenAPI.Checker.PathsPrefixTree as P
26+import OpenAPI.Checker.Subtree
27+import OpenAPI.Checker.Validate.Schema.DNF
28+import OpenAPI.Checker.Validate.Schema.Issues
29+import OpenAPI.Checker.Validate.Schema.JsonFormula
30+import OpenAPI.Checker.Validate.Schema.Traced
31+import OpenAPI.Checker.Validate.Schema.TypedJson
32+import OpenAPI.Checker.Validate.Schema.Partition
33+import OpenAPI.Checker.Validate.Schema.Process
3534
36−checkFormulas ::
37− (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
38− HList xs ->
39− Behavior 'SchemaLevel ->
40− ProdCons (Trace Schema) ->
41− ProdCons (Traced (Definitions Schema)) ->
42− ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel) ->
43− SemanticCompatFormula ()
35+checkFormulas
36+ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
37+ => HList xs
38+ -> Behavior 'SchemaLevel
39+ -> ProdCons (Trace Schema)
40+ -> ProdCons (Traced (Definitions Schema))
41+ -> ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
42+ -> SemanticCompatFormula ()
4443checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
4544 case P.toList ep ++ P.toList ec of
4645 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
4746 [] -> do
4847 -- We have the following isomorphisms:
48+ -- (A ⊂ X ∪ Y) = (A ⊂ X) \/ (A ⊂ Y)
4949 -- (A ⊂ X ∩ Y) = (A ⊂ X) /\ (A ⊂ Y)
5050 -- (A ⊂ ⊤) = 1
5151 -- (X ∪ Y ⊂ B) = (X ⊂ B) /\ (Y ⊂ B)
5252 -- (∅ ⊂ B) = 1
5353 -- The remaining cases are, notably, not isomorphisms:
54− -- 1) (A ⊂ X ∪ Y) <= (A ⊂ X) \/ (A ⊂ Y)
55− -- 2) (A ⊂ ∅) <= 0
56− -- 3) (X ∩ Y ⊂ B) <= (X ⊂ B) \/ (Y ⊂ B)
57− -- 4) (⊤ ⊂ B) <= 0
58− -- Therefore we have the implications with (∃ and ∀ being the N-ary
54+ -- 1) (A ⊂ ∅) <= 0
55+ -- 2) (X ∩ Y ⊂ B) <= (X ⊂ B) \/ (Y ⊂ B)
56+ -- 3) (⊤ ⊂ B) <= 0
57+ -- Therefore we have the isomorphisms with (∃ and ∀ being the N-ary
5958 -- versions of \/ and /\ respectively):
6059 -- (⋃_i ⋂_j A[i,j]) ⊂ (⋃_k ⋂_l B[k,l])
61− -- <= ∃k ∀l ∀i, (⋂_j A[i,j]) ⊂ B[k,l]
60+ -- = ∃k ∀l ∀i, (⋂_j A[i,j]) ⊂ B[k,l]
6261 -- = ∀i ∃k ∀l, (⋂_j A[i,j]) ⊂ B[k,l]
6362 -- with the caveat that the the set over which k ranges is nonempty.
64− -- (because 2) is not an isomorphism), and that this is a sufficient
65− -- but not necessary condition (because 1) is not an isomorphism).
63+ -- This is because 1) is not an isomorphism.
6664 -- Our disjunction loses information, so it makes sense to nest it as
6765 -- deeply as possible, hence we choose the latter representation.
6866 --
6967 -- We delegate the verification of (⋂_j A[j]) ⊂ B to a separate heuristic
7068 -- function, with the understanding that ∃j, A[j] ⊂ B is a sufficient,
71− -- but not necessary condition (because of 3) and 4)).
69+ -- but not necessary condition (because of 2) and 3)).
7270 --
7371 -- If k ranges over an empty set, we have the isomorphism:
7472 -- (⋃_i ⋂_j A[i,j]) ⊂ ∅ = ∀i, (⋂_j A[i,j]) ⊂ ∅
@@ -145,21 +143,21 @@ checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
145143 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
146144 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
147145
148−checkContradiction ::
149− Behavior 'TypedSchemaLevel ->
150− Maybe Partition ->
151− S.Set (Condition t) ->
152− SemanticCompatFormula ()
146+checkContradiction
147+ :: Behavior 'TypedSchemaLevel
148+ -> Maybe Partition
149+ -> S.Set (Condition t)
150+ -> SemanticCompatFormula ()
153151checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
154152
155−checkImplication ::
156− (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
157− HList xs ->
158− Behavior 'TypedSchemaLevel ->
159− ProdCons (Trace Schema) -> -- the traces of the root schemas used in this comparison
160− S.Set (Condition t) ->
161− Condition t ->
162− SemanticCompatFormula ()
153+checkImplication
154+ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
155+ => HList xs
156+ -> Behavior 'TypedSchemaLevel
157+ -> ProdCons (Trace Schema) -- the traces of the root schemas used in this comparison
158+ -> S.Set (Condition t)
159+ -> Condition t
160+ -> SemanticCompatFormula ()
163161checkImplication env beh trs prods cons = case findExactly prods of
164162 Just e
165163 | all (satisfiesTyped e) prods ->
@@ -167,104 +165,124 @@ checkImplication env beh trs prods cons = case findExactly prods of
167165 then pure ()
168166 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
169167 | otherwise -> pure () -- vacuously true
168+
170169 Nothing -> case cons of
171170 -- the above code didn't catch it, so there's no Exactly condition on the lhs
172171 Exactly e -> issueAt beh (NoMatchingEnum $ untypeValue e)
172+
173173 Maximum m -> foldCheck min m NoMatchingMaximum MatchingMaximumWeak $ \case
174174 Maximum m' -> Just m'
175175 _ -> Nothing
176+
176177 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
177178 Minimum m' -> Just m'
178179 _ -> Nothing
180+
179181 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
180182 MultipleOf m' -> Just m'
181183 _ -> Nothing
184+
182185 NumberFormat f -> case flip any prods $ \case
183− NumberFormat f' -> f == f'
184− _ -> False of
186+ NumberFormat f' -> f == f'
187+ _ -> False
188+ of
185189 True -> pure ()
186190 False -> issueAt beh (NoMatchingFormat f)
191+
187192 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
188193 MaxLength m' -> Just m'
189194 _ -> Nothing
195+
190196 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
191197 MinLength m' -> Just m'
192198 _ -> Nothing
199+
193200 Pattern p -> case flip any prods $ \case
194− Pattern p' -> p == p'
195− _ -> False of
201+ Pattern p' -> p == p'
202+ _ -> False
203+ of
196204 True -> pure ()
197205 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
206+
198207 StringFormat f -> case flip any prods $ \case
199− StringFormat f' -> f == f'
200− _ -> False of
208+ StringFormat f' -> f == f'
209+ _ -> False
210+ of
201211 True -> pure ()
202212 False -> issueAt beh (NoMatchingFormat f)
213+
203214 Items _ cons' -> case foldSome (<>) prods $ \case
204− Items _ rs -> Just (Just (rs NE.:| []), mempty)
205− TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
206− _ -> Nothing of
215+ Items _ rs -> Just (Just (rs NE.:| []), mempty)
216+ TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
217+ _ -> Nothing
218+ of
207219 Just (mItems, Just pfs)
208220 | not $ allSame (length <$> pfs) -> pure () -- vacuously
209− | let plen = genericLength (NE.head pfs) ->
210− clarifyIssue (AnItem beh (anIssue TupleToArray)) $
211− for_ [0 .. plen - 1] $ \i -> do
212− let prod' = tracedConjunct $ case mItems of
213− Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
214− Nothing -> (`genericIndex` i) <$> pfs
215− checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
221+ | let plen = genericLength (NE.head pfs)
222+ -> clarifyIssue (AnItem beh (anIssue TupleToArray)) $ for_ [0 .. plen - 1] $ \i -> do
223+ let prod' = tracedConjunct $ case mItems of
224+ Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
225+ Nothing -> (`genericIndex` i) <$> pfs
226+ checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
216227 Just (Just prods', Nothing) -> do
217228 let prod' = tracedConjunct prods'
218229 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
219230 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
220231 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
232+
221233 TupleItems (map snd -> fs) -> case foldSome (<>) prods $ \case
222− TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
223− MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
224− MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
225− Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
226− _ -> Nothing of
227− -- if the length constraints in the producer are contradictory:
228− Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
229− -- We have an explicit tuple items clause...
230− Just (Just pfs, Just (Max plen), _, _)
231− | plen /= genericLength fs -> -- ...of wrong length
232− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
233− | otherwise ->
234− for_ [0 .. plen - 1] $ \i -> do
234+ TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
235+ MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
236+ MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
237+ Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
238+ _ -> Nothing
239+ of
240+ -- if the length constraints in the producer are contradictory:
241+ Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
242+ -- We have an explicit tuple items clause...
243+ Just (Just pfs, Just (Max plen), _, _)
244+ | plen /= genericLength fs -- ...of wrong length
245+ -> issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
246+ | otherwise
247+ -> for_ [0 .. plen - 1] $ \i -> do
235248 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
236− -- We have a fixed length array in the producer...
237− Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
238− | plen == plen' ->
239− clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
240− _
241− | plen /= genericLength fs -> -- ...of wrong length
242− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
249+ -- We have a fixed length array in the producer...
250+ Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
251+ | plen == plen'
252+ -> clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
253+ _ | plen /= genericLength fs -> -- ...of wrong length
254+ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
243255 Just rs -> for_ [0 .. plen - 1] $ \i -> do
244256 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
245257 -- ...and no "items" schema
246258 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
247259 for_ [0 .. plen - 1] $ \i -> do
248260 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
249− _ -> issueAt beh NoMatchingTupleItems
261+ _ -> issueAt beh NoMatchingTupleItems
262+
250263 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
251264 MaxItems m' -> Just m'
252265 TupleItems fs -> Just $ toInteger $ length fs
253266 _ -> Nothing
267+
254268 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
255269 MinItems m' -> Just m'
256270 TupleItems fs -> Just $ toInteger $ length fs
257271 _ -> Nothing
272+
258273 UniqueItems -> case flip any prods $ \case
259− UniqueItems -> True
260− MaxItems 1 -> True
261− TupleItems fs | length fs == 1 -> True
262− _ -> False of
274+ UniqueItems -> True
275+ MaxItems 1 -> True
276+ TupleItems fs | length fs == 1 -> True
277+ _ -> False
278+ of
263279 True -> pure ()
264280 False -> issueAt beh NoMatchingUniqueItems
281+
265282 Properties props _ madd -> case foldSome (<>) prods $ \case
266− Properties props' _ madd' -> Just $ (props', madd') NE.:| []
267− _ -> Nothing of
283+ Properties props' _ madd' -> Just $ (props', madd') NE.:| []
284+ _ -> Nothing
285+ of
268286 Just pm ->
269287 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
270288 NE.toList pm <&> \(props', madd') -> do
@@ -295,9 +313,11 @@ checkImplication env beh trs prods cons = case findExactly prods of
295313 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
296314 pure ()
297315 Nothing -> issueAt beh NoMatchingProperties
316+
298317 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
299318 MaxProperties m' -> Just m'
300319 _ -> Nothing
320+
301321 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
302322 MinProperties m' -> Just m'
303323 _ -> Nothing
@@ -305,14 +325,14 @@ checkImplication env beh trs prods cons = case findExactly prods of
305325 lcmScientific (toRational -> a) (toRational -> b) =
306326 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
307327
308− foldCheck ::
309− Eq a =>
310− (a -> a -> a) ->
311− a ->
312− (a -> Issue 'TypedSchemaLevel) ->
313− (ProdCons a -> Issue 'TypedSchemaLevel) ->
314− (forall t. Condition t -> Maybe a) ->
315− SemanticCompatFormula ()
328+ foldCheck
329+ :: Eq a
330+ => (a -> a -> a)
331+ -> a
332+ -> (a -> Issue 'TypedSchemaLevel)
333+ -> (ProdCons a -> Issue 'TypedSchemaLevel)
334+ -> (forall t. Condition t -> Maybe a)
335+ -> SemanticCompatFormula ()
316336 foldCheck f m missing weak extr = case foldSome f prods extr of
317337 Just m'
318338 | f m' m == m' -> pure ()
src/Data/OpenApi/Compare/Validate/Schema/DNF.hs → src/OpenAPI/Checker/Validate/Schema/DNF.hsrenamed+9−9
@@ -1,12 +1,12 @@
1−module Data.OpenApi.Compare.Validate.Schema.DNF
2− ( DNF (..),
3− Disjunct (..),
4− pattern SingleDisjunct,
5− pattern TopDNF,
6− pattern BottomDNF,
7− pattern LiteralDNF,
8− foldDNF,
9− forDNF,
1+module OpenAPI.Checker.Validate.Schema.DNF
2+ ( DNF (..)
3+ , Disjunct (..)
4+ , pattern SingleDisjunct
5+ , pattern TopDNF
6+ , pattern BottomDNF
7+ , pattern LiteralDNF
8+ , foldDNF
9+ , forDNF
1010 )
1111where
1212
src/Data/OpenApi/Compare/Validate/Schema/Issues.hs → src/OpenAPI/Checker/Validate/Schema/Issues.hsrenamed+39−121
@@ -1,21 +1,21 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.Schema.Issues
4− ( Issue (..),
5− Behave (..),
3+module OpenAPI.Checker.Validate.Schema.Issues
4+ ( Issue (..)
5+ , Behave (..)
66 )
77where
88
99import qualified Data.Aeson as A
1010import Data.OpenApi
11−import Data.OpenApi.Compare.Behavior
12−import Data.OpenApi.Compare.Subtree
13−import Data.OpenApi.Compare.Validate.Schema.JsonFormula
14−import Data.OpenApi.Compare.Validate.Schema.Partition
15−import Data.OpenApi.Compare.Validate.Schema.TypedJson
1611import Data.Scientific
1712import Data.Text (Text)
1813import qualified Data.Text as T
14+import OpenAPI.Checker.Behavior
15+import OpenAPI.Checker.Subtree
16+import OpenAPI.Checker.Validate.Schema.JsonFormula
17+import OpenAPI.Checker.Validate.Schema.Partition
18+import OpenAPI.Checker.Validate.Schema.TypedJson
1919import Text.Pandoc.Builder hiding (Format, Null)
2020
2121instance Issuable 'TypedSchemaLevel where
@@ -87,99 +87,33 @@ instance Issuable 'TypedSchemaLevel where
8787 | -- | consumer indicates that no values in a particular partition are allowed, but we weren't able to conclude this in the producer
8888 PartitionBecomesEmpty Partition
8989 deriving stock (Eq, Ord, Show)
90− issueKind = \case
91− NoMatchingEnum _ -> ProbablyIssue
92− MatchingMaximumWeak _ -> ProbablyIssue -- interplay with MultipleOf could make this not an issue
93− MatchingMinimumWeak _ -> ProbablyIssue -- ditto
94− MatchingMultipleOfWeak _ -> ProbablyIssue -- ditto
95− NoMatchingFormat _ -> Unsupported
96− NoMatchingPattern _ -> Unsupported
97− ArrayToTuple -> Comment
98− TupleToArray -> Comment
99− NoMatchingProperties -> ProbablyIssue -- TODO: #109
100− TypeBecomesEmpty -> ProbablyIssue -- TODO: #70
101− PartitionBecomesEmpty _ -> ProbablyIssue -- ditto
102− _ -> CertainIssue
103− relatedIssues =
104− (==) `withClass` \case
105− EnumDoesntSatisfy v -> Just v
106− NoMatchingEnum v -> Just v
107− _ -> Nothing
108− `withClass` \case
109− NoMatchingMaximum _ -> Just ()
110− MatchingMaximumWeak _ -> Just ()
111− _ -> Nothing
112− `withClass` \case
113− NoMatchingMinimum _ -> Just ()
114− MatchingMinimumWeak _ -> Just ()
115− _ -> Nothing
116− `withClass` \case
117− NoMatchingMultipleOf _ -> Just ()
118− MatchingMultipleOfWeak _ -> Just ()
119− _ -> Nothing
120− `withClass` \case
121− NoMatchingMaxLength _ -> Just ()
122− MatchingMaxLengthWeak _ -> Just ()
123− _ -> Nothing
124− `withClass` \case
125− NoMatchingMinLength _ -> Just ()
126− MatchingMinLengthWeak _ -> Just ()
127− _ -> Nothing
128− `withClass` \case
129− NoMatchingItems -> Just ()
130− ArrayToTuple -> Just ()
131− TupleToArray -> Just ()
132− NoMatchingTupleItems -> Just ()
133− TupleItemsLengthChanged _ -> Just ()
134− _ -> Nothing
135− `withClass` \case
136− NoMatchingMaxItems _ -> Just ()
137− MatchingMaxItemsWeak _ -> Just ()
138− _ -> Nothing
139− `withClass` \case
140− NoMatchingMinItems _ -> Just ()
141− MatchingMinItemsWeak _ -> Just ()
142− _ -> Nothing
143− `withClass` \case
144− NoMatchingMaxProperties _ -> Just ()
145− MatchingMaxPropertiesWeak _ -> Just ()
146− _ -> Nothing
147− `withClass` \case
148− NoMatchingMinProperties _ -> Just ()
149− MatchingMinPropertiesWeak _ -> Just ()
150− _ -> Nothing
90+ issueIsUnsupported _ = False
15191 describeIssue Forward (EnumDoesntSatisfy v) = para "The following enum value was removed:" <> showJSONValue v
15292 describeIssue Backward (EnumDoesntSatisfy v) = para "The following enum value was added:" <> showJSONValue v
15393 describeIssue Forward (NoMatchingEnum v) = para "The following enum value has been added:" <> showJSONValue v
15494 describeIssue Backward (NoMatchingEnum v) = para "The following enum value has been removed:" <> showJSONValue v
15595 describeIssue Forward (NoMatchingMaximum b) = para $ "Upper bound has been added:" <> showBound b <> "."
15696 describeIssue Backward (NoMatchingMaximum b) = para $ "Upper bound has been removed:" <> showBound b <> "."
157− describeIssue ori (MatchingMaximumWeak (orientProdCons ori -> ProdCons p c)) =
158− para $ "Upper bound changed from " <> showBound p <> " to " <> showBound c <> "."
97+ describeIssue _ (MatchingMaximumWeak (ProdCons p c)) = para $ "Upper bound changed from " <> showBound p <> " to " <> showBound c <> "."
15998 describeIssue Forward (NoMatchingMinimum b) = para $ "Lower bound has been added: " <> showBound b <> "."
16099 describeIssue Backward (NoMatchingMinimum b) = para $ "Lower bound has been removed: " <> showBound b <> "."
161− describeIssue ori (MatchingMinimumWeak (orientProdCons ori -> ProdCons p c)) =
162− para $ "Lower bound changed from " <> showBound p <> " to " <> showBound c <> "."
100+ describeIssue _ (MatchingMinimumWeak (ProdCons p c)) = para $ "Lower bound changed from " <> showBound p <> " to " <> showBound c <> "."
163101 describeIssue Forward (NoMatchingMultipleOf n) = para $ "Value is now a multiple of " <> show' n <> "."
164102 describeIssue Backward (NoMatchingMultipleOf n) = para $ "Value is no longer a multiple of " <> show' n <> "."
165− describeIssue ori (MatchingMultipleOfWeak (orientProdCons ori -> ProdCons p c)) =
166− para $ "Value changed from being a multiple of " <> show' p <> " to being a multiple of " <> show' c <> "."
103+ describeIssue _ (MatchingMultipleOfWeak (ProdCons p c)) = para $ "Value changed from being a multiple of " <> show' p <> " to being a multiple of " <> show' c <> "."
167104 describeIssue Forward (NoMatchingFormat f) = para $ "Format added: " <> code f <> "."
168105 describeIssue Backward (NoMatchingFormat f) = para $ "Format removed: " <> code f <> "."
169106 describeIssue Forward (NoMatchingMaxLength n) = para $ "Maximum length added: " <> show' n <> "."
170107 describeIssue Backward (NoMatchingMaxLength n) = para $ "Maximum length removed: " <> show' n <> "."
171− describeIssue ori (MatchingMaxLengthWeak (orientProdCons ori -> ProdCons p c)) =
172− para $ "Maximum length of the string changed from " <> show' p <> " to " <> show' c <> "."
108+ describeIssue _ (MatchingMaxLengthWeak (ProdCons p c)) = para $ "Maximum length of the string changed from " <> show' p <> " to " <> show' c <> "."
173109 describeIssue Forward (NoMatchingMinLength n) = para $ "Minimum length of the string added: " <> show' n <> "."
174110 describeIssue Backward (NoMatchingMinLength n) = para $ "Minimum length of the string removed: " <> show' n <> "."
175− describeIssue ori (MatchingMinLengthWeak (orientProdCons ori -> ProdCons p c)) =
176− para $ "Minimum length of the string changed from " <> show' p <> " to " <> show' c <> "."
111+ describeIssue _ (MatchingMinLengthWeak (ProdCons p c)) = para $ "Minimum length of the string changed from " <> show' p <> " to " <> show' c <> "."
177112 describeIssue Forward (NoMatchingPattern p) = para "Pattern (regular expression) added: " <> codeBlock p
178113 describeIssue Backward (NoMatchingPattern p) = para "Pattern (regular expression) removed: " <> codeBlock p
179114 describeIssue Forward NoMatchingItems = para "Array item schema has been added."
180115 describeIssue Backward NoMatchingItems = para "Array item schema has been removed."
181− describeIssue ori (TupleItemsLengthChanged (orientProdCons ori -> ProdCons p c)) =
182− para $ "Tuple length changed from " <> show' p <> " to " <> show' c <> "."
116+ describeIssue _ (TupleItemsLengthChanged (ProdCons p c)) = para $ "Tuple length changed from " <> show' p <> " to " <> show' c <> "."
183117 describeIssue Forward ArrayToTuple = para "The array is now explicitly defined as a tuple."
184118 describeIssue Backward ArrayToTuple = para "The array is no longer explicitly defined as a tuple."
185119 describeIssue Forward TupleToArray = para "The array is no longer explicitly defined as a tuple."
@@ -188,12 +122,10 @@ instance Issuable 'TypedSchemaLevel where
188122 describeIssue Backward NoMatchingTupleItems = para "The array is no longer explicitly defined as a tuple."
189123 describeIssue Forward (NoMatchingMaxItems n) = para $ "Maximum length of the array has been added " <> show' n <> "."
190124 describeIssue Backward (NoMatchingMaxItems n) = para $ "Maximum length of the array has been removed " <> show' n <> "."
191− describeIssue ori (MatchingMaxItemsWeak (orientProdCons ori -> ProdCons p c)) =
192− para $ "Maximum length of the array changed from " <> show' p <> " to " <> show' c <> "."
125+ describeIssue _ (MatchingMaxItemsWeak (ProdCons p c)) = para $ "Maximum length of the array changed from " <> show' p <> " to " <> show' c <> "."
193126 describeIssue Forward (NoMatchingMinItems n) = para $ "Minimum length of the array added: " <> show' n <> "."
194127 describeIssue Backward (NoMatchingMinItems n) = para $ "Minimum length of the array removed: " <> show' n <> "."
195− describeIssue ori (MatchingMinItemsWeak (orientProdCons ori -> ProdCons p c)) =
196− para $ "Minimum length of the array changed from " <> show' p <> " to " <> show' c <> "."
128+ describeIssue _ (MatchingMinItemsWeak (ProdCons p c)) = para $ "Minimum length of the array changed from " <> show' p <> " to " <> show' c <> "."
197129 describeIssue Forward NoMatchingUniqueItems = para "Items are now required to be unique."
198130 describeIssue Backward NoMatchingUniqueItems = para "Items are no longer required to be unique."
199131 describeIssue Forward NoMatchingProperties = para "Property added."
@@ -202,20 +134,17 @@ instance Issuable 'TypedSchemaLevel where
202134 describeIssue Backward NoAdditionalProperties = para "Additional properties have been added."
203135 describeIssue Forward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been added: " <> show' n <> "."
204136 describeIssue Backward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been removed: " <> show' n <> "."
205− describeIssue ori (MatchingMaxPropertiesWeak (orientProdCons ori -> ProdCons p c)) =
206− para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
137+ describeIssue _ (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
207138 describeIssue Forward (NoMatchingMinProperties n) = para $ "Minimum number of properties added: " <> show' n <> "."
208139 describeIssue Backward (NoMatchingMinProperties n) = para $ "Minimum number of properties removed: " <> show' n <> "."
209− describeIssue ori (MatchingMinPropertiesWeak (orientProdCons ori -> ProdCons p c)) =
210− para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
140+ describeIssue _ (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
211141 describeIssue _ (NoMatchingCondition mPart conds) =
212142 para
213− ( case mPart of
214− Nothing -> "Could not verify that the following conditions hold (please file a bug if you see this):"
215− Just locPart ->
216− "In cases where " <> showPartition locPart
217− <> " – could not verify that the following conditions hold (please file a bug if you see this):"
218− )
143+ (case mPart of
144+ Nothing -> "Could not verify that the following conditions hold (please file a bug if you see this):"
145+ Just locPart ->
146+ "In cases where " <> showPartition locPart
147+ <> " – could not verify that the following conditions hold (please file a bug if you see this):")
219148 <> bulletList ((\(SomeCondition c) -> showCondition c) <$> conds)
220149 describeIssue Forward TypeBecomesEmpty = para "The type has been removed."
221150 describeIssue Backward TypeBecomesEmpty = para "The type has been added."
@@ -246,26 +175,15 @@ instance Issuable 'SchemaLevel where
246175 | -- | producer allows a property that is not allowed in the consumer
247176 UnexpectedProperty
248177 deriving stock (Eq, Ord, Show)
249− issueKind = \case
250− NotSupported _ -> Unsupported
251− OneOfNotDisjoint -> Unsupported
252− InvalidSchema _ -> SchemaInvalid
253− UnguardedRecursion -> Unsupported
254− AdditionalToProperty -> Comment
255− PropertyToAdditional -> Comment
256− TypesRestricted _ -> ProbablyIssue -- TODO: #70
257− _ -> CertainIssue
258− relatedIssues =
259− (==) `withClass` \case
260− AdditionalToProperty -> Just ()
261− PropertyToAdditional -> Just ()
262− _ -> Nothing
263− `withClass` \case
264− PropertyNowRequired -> Just ()
265− UnexpectedProperty -> Just ()
266− _ -> Nothing
178+ issueIsUnsupported = \case
179+ NotSupported _ -> True
180+ OneOfNotDisjoint -> True
181+ InvalidSchema _ -> True
182+ UnguardedRecursion -> True
183+ _ -> False
184+
267185 describeIssue _ (NotSupported i) =
268− para (emph "Encountered a feature that CompaREST does not support: " <> text i <> ".")
186+ para (emph "Encountered a feature that OpenApi Diff does not support: " <> text i <> ".")
269187 describeIssue _ OneOfNotDisjoint =
270188 para $
271189 "Could not deduce that " <> code "oneOf"
@@ -277,7 +195,7 @@ instance Issuable 'SchemaLevel where
277195 describeIssue _ (InvalidSchema i) =
278196 para (emph "The schema is invalid: " <> text i <> ".")
279197 describeIssue _ UnguardedRecursion =
280− para "Encountered recursion that is too complex for CompaREST to untangle."
198+ para "Encountered recursion that is too complex for OpenApi Diff to untangle."
281199 describeIssue Forward (TypesRestricted tys) = case tys of
282200 [] -> para "No longer has any valid values." -- weird
283201 _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
@@ -298,14 +216,14 @@ instance Behavable 'SchemaLevel 'TypedSchemaLevel where
298216 = OfType JsonType
299217 deriving stock (Eq, Ord, Show)
300218
301− describeBehavior (OfType t) = describeJSONType t
219+ describeBehaviour (OfType t) = describeJSONType t
302220
303221instance Behavable 'TypedSchemaLevel 'TypedSchemaLevel where
304222 data Behave 'TypedSchemaLevel 'TypedSchemaLevel
305223 = InPartition Partition
306224 deriving stock (Eq, Ord, Show)
307225
308− describeBehavior (InPartition partition) = "In cases where " <> showPartition partition
226+ describeBehaviour (InPartition partition) = "In cases where " <> showPartition partition
309227
310228instance Behavable 'TypedSchemaLevel 'SchemaLevel where
311229 data Behave 'TypedSchemaLevel 'SchemaLevel
@@ -315,7 +233,7 @@ instance Behavable 'TypedSchemaLevel 'SchemaLevel where
315233 | InAdditionalProperty
316234 deriving stock (Eq, Ord, Show)
317235
318− describeBehavior InItems = "Items"
319− describeBehavior (InItem i) = "Item " <> show' i
320− describeBehavior (InProperty p) = "Property " <> code p
321− describeBehavior InAdditionalProperty = "Additional properties"
236+ describeBehaviour InItems = "Items"
237+ describeBehaviour (InItem i) = "Item " <> show' i
238+ describeBehaviour (InProperty p) = "Property " <> code p
239+ describeBehaviour InAdditionalProperty = "Additional properties"
src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hs → src/OpenAPI/Checker/Validate/Schema/JsonFormula.hsrenamed+53−61
@@ -1,37 +1,32 @@
1−module Data.OpenApi.Compare.Validate.Schema.JsonFormula
2− ( Bound (..),
3− showBound,
4− Property (..),
5− Condition (..),
6− showCondition,
7− satisfiesTyped,
8− checkStringFormat,
9− checkNumberFormat,
10− SomeCondition (..),
11− JsonFormula (..),
12− satisfiesFormula,
13− satisfies,
14− showJSONValue,
15− showJSONValueInline,
1+module OpenAPI.Checker.Validate.Schema.JsonFormula
2+ ( Bound (..)
3+ , showBound
4+ , Property (..)
5+ , Condition (..)
6+ , showCondition
7+ , satisfiesTyped
8+ , checkStringFormat
9+ , checkNumberFormat
10+ , SomeCondition (..)
11+ , JsonFormula (..)
12+ , satisfiesFormula
13+ , satisfies
14+ , showJSONValue
15+ , showJSONValueInline
1616 )
1717where
1818
1919import Algebra.Lattice
2020import qualified Data.Aeson as A
21−import qualified Data.Aeson.Key as Key
22−import qualified Data.Aeson.KeyMap as KeyMap
2321import qualified Data.ByteString.Lazy as BSL
2422import qualified Data.Foldable as F
23+import qualified Data.HashMap.Strict as HM
2524import Data.Functor
2625import Data.Int
2726import Data.Kind
2827import qualified Data.Map as M
2928import Data.Maybe
3029import Data.OpenApi
31−import Data.OpenApi.Compare.Orphans ()
32−import Data.OpenApi.Compare.Subtree
33−import Data.OpenApi.Compare.Validate.Schema.DNF
34−import Data.OpenApi.Compare.Validate.Schema.TypedJson
3530import Data.Ord
3631import Data.Ratio
3732import Data.Scientific
@@ -40,7 +35,11 @@ import Data.Text (Text)
4035import qualified Data.Text as T
4136import qualified Data.Text.Encoding as T
4237import Data.Typeable
43−import Text.Pandoc.Builder hiding (Format)
38+import OpenAPI.Checker.Orphans ()
39+import OpenAPI.Checker.Subtree
40+import OpenAPI.Checker.Validate.Schema.DNF
41+import OpenAPI.Checker.Validate.Schema.TypedJson
42+import Text.Pandoc.Builder hiding (Format, Null)
4443import Text.Regex.Pcre2
4544
4645data Bound a = Exclusive !a | Inclusive !a
@@ -68,33 +67,30 @@ data Property = Property
6867data Condition :: JsonType -> Type where
6968 Exactly :: TypedValue t -> Condition t
7069 Maximum :: !(Bound Scientific) -> Condition 'Number
71− Minimum ::
72− !(Down (Bound (Down Scientific))) ->
73− -- | this has the right Ord
74− Condition 'Number
70+ Minimum
71+ :: !(Down (Bound (Down Scientific)))
72+ -> Condition 'Number -- ^ this has the right Ord
7573 MultipleOf :: !Scientific -> Condition 'Number
7674 NumberFormat :: !Format -> Condition 'Number
7775 MaxLength :: !Integer -> Condition 'String
7876 MinLength :: !Integer -> Condition 'String
7977 Pattern :: !Pattern -> Condition 'String
8078 StringFormat :: !Format -> Condition 'String
81− Items ::
82− !(ForeachType JsonFormula) ->
83− !(Traced (Referenced Schema)) ->
84− Condition 'Array
85− TupleItems ::
86− ![(ForeachType JsonFormula, Traced (Referenced Schema))] ->
87− Condition 'Array
79+ Items
80+ :: !(ForeachType JsonFormula)
81+ -> !(Traced (Referenced Schema))
82+ -> Condition 'Array
83+ TupleItems
84+ :: ![(ForeachType JsonFormula, Traced (Referenced Schema))]
85+ -> Condition 'Array
8886 MaxItems :: !Integer -> Condition 'Array
8987 MinItems :: !Integer -> Condition 'Array
9088 UniqueItems :: Condition 'Array
91− Properties ::
92− !(M.Map Text Property) ->
93− -- | formula for additional properties
94− !(ForeachType JsonFormula) ->
95− -- | schema for additional properties, Nothing means bottom
96− !(Maybe (Traced (Referenced Schema))) ->
97− Condition 'Object
89+ Properties
90+ :: !(M.Map Text Property)
91+ -> !(ForeachType JsonFormula) -- ^ formula for additional properties
92+ -> !(Maybe (Traced (Referenced Schema))) -- ^ schema for additional properties, Nothing means bottom
93+ -> Condition 'Object
9894 MaxProperties :: !Integer -> Condition 'Object
9995 MinProperties :: !Integer -> Condition 'Object
10096
@@ -122,15 +118,13 @@ showCondition = \case
122118 UniqueItems -> para "The elements in the array should be unique."
123119 (Properties props additional _) ->
124120 bulletList $
125− ( M.toList props
126− <&> ( \(k, p) ->
127− para (code k)
128− <> para (strong $ if propRequired p then "Required" else "Optional")
129− <> showForEachJsonFormula (propFormula p)
130− )
131− )
121+ (M.toList props
122+ <&> (\(k, p) ->
123+ para (code k)
124+ <> para (strong $ if propRequired p then "Required" else "Optional")
125+ <> showForEachJsonFormula (propFormula p)))
132126 <> [ para (emph "Additional properties")
133− <> showForEachJsonFormula additional
127+ <> showForEachJsonFormula additional
134128 ]
135129 (MaxProperties n) -> para $ "The maximum number of fields should be " <> show' n <> "."
136130 (MinProperties n) -> para $ "The minimum number of fields should be " <> show' n <> "."
@@ -139,17 +133,15 @@ showCondition = \case
139133 showForEachJsonFormula i =
140134 bulletList $
141135 foldType
142− ( \t f -> case getJsonFormula $ f i of
143− BottomDNF -> mempty
144− (DNF conds) ->
136+ (\t f -> case getJsonFormula $ f i of
137+ BottomDNF -> mempty
138+ (DNF conds) ->
145139 [ para (describeJSONType t)
146140 <> bulletList
147− ( S.toList conds <&> \case
148− Disjunct (S.toList -> []) -> para "Empty"
149− Disjunct (S.toList -> cond) -> bulletList (showCondition <$> cond)
150− )
151− ]
152− )
141+ (S.toList conds <&> \case
142+ Disjunct (S.toList -> []) -> para "Empty"
143+ Disjunct (S.toList -> cond) -> bulletList (showCondition <$> cond))
144+ ])
153145
154146showJSONValue :: A.Value -> Blocks
155147showJSONValue v = codeBlockWith ("", ["json"], mempty) (T.decodeUtf8 . BSL.toStrict . A.encode $ v)
@@ -178,10 +170,10 @@ satisfiesTyped (TArray a) (MaxItems m) = fromIntegral (F.length a) <= m
178170satisfiesTyped (TArray a) (MinItems m) = fromIntegral (F.length a) >= m
179171satisfiesTyped (TArray a) UniqueItems = S.size (S.fromList $ F.toList a) == F.length a -- TODO: could be better #36
180172satisfiesTyped (TObject o) (Properties props additional _) =
181− all (`KeyMap.member` o) (map Key.fromText $ M.keys (M.filter propRequired props))
182− && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup (Key.toText k) props) (KeyMap.toList o)
183−satisfiesTyped (TObject o) (MaxProperties m) = fromIntegral (KeyMap.size o) <= m
184−satisfiesTyped (TObject o) (MinProperties m) = fromIntegral (KeyMap.size o) >= m
173+ all (`HM.member` o) (M.keys (M.filter propRequired props))
174+ && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup k props) (HM.toList o)
175+satisfiesTyped (TObject o) (MaxProperties m) = fromIntegral (HM.size o) <= m
176+satisfiesTyped (TObject o) (MinProperties m) = fromIntegral (HM.size o) >= m
185177
186178checkNumberFormat :: Format -> Scientific -> Bool
187179checkNumberFormat "int32" (toRational -> n) =
src/Data/OpenApi/Compare/Validate/Schema/Partition.hs → src/OpenAPI/Checker/Validate/Schema/Partition.hsrenamed+38−42
@@ -1,22 +1,21 @@
1−module Data.OpenApi.Compare.Validate.Schema.Partition
2− ( partitionSchema,
3− partitionRefSchema,
4− selectPartition,
5− runPartitionM,
6− tryPartition,
7− showPartition,
8− intersectSchema,
9− intersectRefSchema,
10− IntersectionResult (..),
11− runIntersectionM,
12− Partition,
1+module OpenAPI.Checker.Validate.Schema.Partition
2+ ( partitionSchema
3+ , partitionRefSchema
4+ , selectPartition
5+ , runPartitionM
6+ , tryPartition
7+ , showPartition
8+ , intersectSchema
9+ , intersectRefSchema
10+ , IntersectionResult (..)
11+ , runIntersectionM
12+ , Partition
1313 )
1414where
1515
1616import Algebra.Lattice
1717import Algebra.Lattice.Lifted
1818import Control.Applicative
19−import Control.Monad (when)
2019import Control.Monad.Reader hiding (ask)
2120import qualified Control.Monad.Reader as R
2221import Control.Monad.State
@@ -32,21 +31,19 @@ import qualified Data.List as L
3231import qualified Data.List.NonEmpty as NE
3332import qualified Data.Map as M
3433import Data.Maybe
35−import Data.Monoid
3634import Data.OpenApi
37−import Data.OpenApi.Compare.Memo
38−import Data.OpenApi.Compare.References
39−import Data.OpenApi.Compare.Subtree
40−import Data.OpenApi.Compare.Validate.Schema.DNF
41−import Data.OpenApi.Compare.Validate.Schema.JsonFormula
42−import Data.OpenApi.Compare.Validate.Schema.Traced
43−import Data.OpenApi.Compare.Validate.Schema.TypedJson
4435import Data.Ord
4536import qualified Data.Set as S
4637import Data.Text (Text)
4738import qualified Data.Text as T
48−import Data.Traversable (forM)
49−import Text.Pandoc.Builder hiding (Format)
39+import OpenAPI.Checker.Memo
40+import OpenAPI.Checker.References
41+import OpenAPI.Checker.Subtree
42+import OpenAPI.Checker.Validate.Schema.DNF
43+import OpenAPI.Checker.Validate.Schema.JsonFormula
44+import OpenAPI.Checker.Validate.Schema.Traced
45+import OpenAPI.Checker.Validate.Schema.TypedJson
46+import Text.Pandoc.Builder hiding (Format, Null)
5047
5148data PartitionData
5249 = DByEnumValue (DNF (S.Set A.Value))
@@ -146,11 +143,10 @@ partitionCondition = \case
146143 Just _ -> top
147144 Nothing ->
148145 singletonPart $
149− DByProperties $
150− LiteralDNF
151− ( M.keysSet $ M.filter (not . propRequired) props
152− , M.keysSet $ M.filter propRequired props
153− )
146+ DByProperties $ LiteralDNF
147+ ( M.keysSet $ M.filter (not . propRequired) props
148+ , M.keysSet $ M.filter propRequired props
149+ )
154150 inProps <- forM (M.toList $ M.filter propRequired props) $ \(k, prop) -> do
155151 f <- partitionRefSchema $ propRefSchema prop
156152 pure $ fmap (\(Partitions m) -> Partitions $ M.mapKeysMonotonic (PInProperty k) m) f
@@ -160,10 +156,10 @@ partitionCondition = \case
160156runPartitionM :: Traced (Definitions Schema) -> PartitionM a -> a
161157runPartitionM defs = runIdentity . runMemo () . (`runReaderT` defs)
162158
163−partitionJsonFormulas ::
164− ProdCons (Traced (Definitions Schema)) ->
165− ProdCons (JsonFormula t) ->
166− Lifted Partitions
159+partitionJsonFormulas
160+ :: ProdCons (Traced (Definitions Schema))
161+ -> ProdCons (JsonFormula t)
162+ -> Lifted Partitions
167163partitionJsonFormulas defs pc = producer pcPart \/ consumer pcPart
168164 where
169165 pcPart = partitionFormula <$> defs <*> pc
@@ -215,11 +211,11 @@ runIntersectionM defs act = case runWriterT $ runReaderT act defs of
215211 Just (x, Any False) -> Same x
216212 Just (x, Any True) -> New x
217213
218−intersectSchema ::
219− PartitionLocation ->
220− PartitionChoice ->
221− Traced Schema ->
222− IntersectionM Schema
214+intersectSchema
215+ :: PartitionLocation
216+ -> PartitionChoice
217+ -> Traced Schema
218+ -> IntersectionM Schema
223219intersectSchema loc part sch = do
224220 allOf' <- forM (tracedAllOf sch) $ \rss ->
225221 -- Assuming i ranges over a nonempty set (checked in processSchema)
@@ -262,11 +258,11 @@ intersectSchema loc part sch = do
262258 pure $ sch' {_schemaEnum = Just enum'}
263259 CByProperties {} -> error "CByProperties not implemented"
264260
265−intersectRefSchema ::
266− PartitionLocation ->
267− PartitionChoice ->
268− Traced (Referenced Schema) ->
269− IntersectionM (Referenced Schema)
261+intersectRefSchema
262+ :: PartitionLocation
263+ -> PartitionChoice
264+ -> Traced (Referenced Schema)
265+ -> IntersectionM (Referenced Schema)
270266intersectRefSchema loc part rs = do
271267 defs <- R.ask
272268 Inline <$> intersectSchema loc part (dereference defs rs)
src/Data/OpenApi/Compare/Validate/Schema/Process.hs → src/OpenAPI/Checker/Validate/Schema/Process.hsrenamed+61−60
@@ -1,11 +1,11 @@
1−module Data.OpenApi.Compare.Validate.Schema.Process
2− ( schemaToFormula,
1+module OpenAPI.Checker.Validate.Schema.Process
2+ ( schemaToFormula
33 )
44where
55
66import Algebra.Lattice
7−import Control.Monad.Reader hiding (ask)
87import qualified Control.Monad.Reader as R
8+import Control.Monad.Reader hiding (ask)
99import Control.Monad.State
1010import Control.Monad.Writer
1111import qualified Data.Aeson as A
@@ -13,23 +13,21 @@ import Data.Functor.Identity
1313import qualified Data.HashMap.Strict.InsOrd as IOHM
1414import qualified Data.Map as M
1515import Data.Maybe
16−import Data.Monoid
1716import Data.OpenApi hiding (get)
18−import Data.OpenApi.Compare.Behavior
19−import Data.OpenApi.Compare.Memo
20−import Data.OpenApi.Compare.Paths
21−import qualified Data.OpenApi.Compare.PathsPrefixTree as P
22−import Data.OpenApi.Compare.References
23−import Data.OpenApi.Compare.Subtree
24−import Data.OpenApi.Compare.Validate.Schema.DNF
25−import Data.OpenApi.Compare.Validate.Schema.Issues
26−import Data.OpenApi.Compare.Validate.Schema.JsonFormula
27−import Data.OpenApi.Compare.Validate.Schema.Partition
28−import Data.OpenApi.Compare.Validate.Schema.Traced
29−import Data.OpenApi.Compare.Validate.Schema.TypedJson
3017import Data.Ord
3118import qualified Data.Set as S
32−import Data.Traversable (forM)
19+import OpenAPI.Checker.Behavior
20+import OpenAPI.Checker.Memo
21+import OpenAPI.Checker.Paths
22+import qualified OpenAPI.Checker.PathsPrefixTree as P
23+import OpenAPI.Checker.References
24+import OpenAPI.Checker.Subtree
25+import OpenAPI.Checker.Validate.Schema.DNF
26+import OpenAPI.Checker.Validate.Schema.Issues
27+import OpenAPI.Checker.Validate.Schema.JsonFormula
28+import OpenAPI.Checker.Validate.Schema.Partition
29+import OpenAPI.Checker.Validate.Schema.Traced
30+import OpenAPI.Checker.Validate.Schema.TypedJson
3331
3432-- | A fake writer monad that doesn't actually record anything and allows lazy recursion.
3533newtype Silent w a = Silent {runSilent :: a}
@@ -41,11 +39,15 @@ instance Monoid w => MonadWriter w (Silent w) where
4139 listen (Silent x) = Silent (x, mempty)
4240 pass (Silent (x, _)) = Silent x
4341
42+instance MonadState (MemoState ()) (Silent w) where
43+ get = Silent $ runIdentity $ runMemo () get
44+ put _ = pure ()
45+
4446type ProcessM = StateT (MemoState ()) (ReaderT (Traced (Definitions Schema)) (Writer (P.PathsPrefixTree Behave AnIssue 'SchemaLevel)))
4547
46−type SilentM = StateT (MemoState ()) (ReaderT (Traced (Definitions Schema)) (Silent (P.PathsPrefixTree Behave AnIssue 'SchemaLevel)))
48+type SilentM = ReaderT (Traced (Definitions Schema)) (Silent (P.PathsPrefixTree Behave AnIssue 'SchemaLevel))
4749
48−-- Either ProcessM or SilentM
50+-- Either SilentM or ProcessM
4951type MonadProcess m =
5052 ( MonadReader (Traced (Definitions Schema)) m
5153 , MonadWriter (P.PathsPrefixTree Behave AnIssue 'SchemaLevel) m
@@ -55,11 +57,11 @@ type MonadProcess m =
5557warn :: MonadProcess m => Issue 'SchemaLevel -> m ()
5658warn issue = tell $ P.singleton $ AnItem Root $ anIssue issue
5759
58−-- Perform a computation lazily, ignoring the warnings and discarding memoization/loop detection information.
59−lazily :: MonadProcess m => SilentM a -> m a
60−lazily m = do
60+-- | Ignore warnings but allow a recursive loop that lazily computes a recursive 'Condition'.
61+silently :: MonadProcess m => SilentM a -> m a
62+silently m = do
6163 defs <- R.ask
62− pure $ runSilent $ runReaderT (runMemo () m) defs
64+ pure . runSilent $ runReaderT m defs
6365
6466warnKnot :: MonadProcess m => KnotTier (ForeachType JsonFormula) () m
6567warnKnot =
@@ -69,10 +71,10 @@ warnKnot =
6971 , tieKnot = \_ -> pure
7072 }
7173
72−processRefSchema ::
73− MonadProcess m =>
74− Traced (Referenced Schema) ->
75− m (ForeachType JsonFormula)
74+processRefSchema
75+ :: MonadProcess m
76+ => Traced (Referenced Schema)
77+ -> m (ForeachType JsonFormula)
7678processRefSchema x = do
7779 defs <- R.ask
7880 memoWithKnot warnKnot (processSchema $ dereference defs x) (ask x)
@@ -80,10 +82,10 @@ processRefSchema x = do
8082-- | Turn a schema into a tuple of 'JsonFormula's that describes the condition
8183-- for every possible type of a JSON value. The conditions are independent, and
8284-- are thus checked independently.
83−processSchema ::
84− MonadProcess m =>
85− Traced Schema ->
86− m (ForeachType JsonFormula)
85+processSchema
86+ :: MonadProcess m
87+ => Traced Schema
88+ -> m (ForeachType JsonFormula)
8789processSchema sch@(extract -> Schema {..}) = do
8890 let singletonFormula :: Condition t -> JsonFormula t
8991 singletonFormula = JsonFormula . LiteralDNF
@@ -241,11 +243,11 @@ processSchema sch@(extract -> Schema {..}) = do
241243 itemsClause <- case tracedItems sch of
242244 Nothing -> pure top
243245 Just (Left rs) -> do
244− f <- lazily $ processRefSchema rs
246+ f <- silently $ processRefSchema rs
245247 pure top {forArray = singletonFormula $ Items f rs}
246248 Just (Right rss) -> do
247249 fsrs <- forM rss $ \rs -> do
248− f <- lazily $ processRefSchema rs
250+ f <- silently $ processRefSchema rs
249251 pure (f, rs)
250252 pure top {forArray = singletonFormula $ TupleItems fsrs}
251253
@@ -271,12 +273,12 @@ processSchema sch@(extract -> Schema {..}) = do
271273 _ -> top
272274
273275 (addProps, addPropSchema) <- case tracedAdditionalProperties sch of
274− Just (Right rs) -> (,Just rs) <$> lazily (processRefSchema rs)
276+ Just (Right rs) -> (,Just rs) <$> silently (processRefSchema rs)
275277 Just (Left False) -> pure (bottom, Nothing)
276278 _ -> pure (top, Just $ traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty)
277279 propList <- forM (S.toList . S.fromList $ IOHM.keys _schemaProperties <> _schemaRequired) $ \k -> do
278280 (f, psch) <- case IOHM.lookup k $ tracedProperties sch of
279− Just rs -> (,rs) <$> lazily (processRefSchema rs)
281+ Just rs -> (,rs) <$> silently (processRefSchema rs)
280282 Nothing ->
281283 let fakeSchema = traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty
282284 in -- The mempty here is incorrect, but if addPropSchema was Nothing, then
@@ -329,27 +331,26 @@ processSchema sch@(extract -> Schema {..}) = do
329331 pure $
330332 nullableClause
331333 \/ meets
332− ( allClauses
333− <> [ anyClause
334− , oneClause
335− , typeClause
336− , enumClause
337− , maximumClause
338− , minimumClause
339− , multipleOfClause
340− , formatClause
341− , maxLengthClause
342− , minLengthClause
343− , patternClause
344− , itemsClause
345− , maxItemsClause
346− , minItemsClause
347− , uniqueItemsClause
348− , propertiesClause
349− , maxPropertiesClause
350− , minPropertiesClause
351− ]
352− )
334+ (allClauses
335+ <> [ anyClause
336+ , oneClause
337+ , typeClause
338+ , enumClause
339+ , maximumClause
340+ , minimumClause
341+ , multipleOfClause
342+ , formatClause
343+ , maxLengthClause
344+ , minLengthClause
345+ , patternClause
346+ , itemsClause
347+ , maxItemsClause
348+ , minItemsClause
349+ , uniqueItemsClause
350+ , propertiesClause
351+ , maxPropertiesClause
352+ , minPropertiesClause
353+ ])
353354
354355{- TODO: ReadOnly/WriteOnly #68 -}
355356
@@ -368,8 +369,8 @@ checkOneOfDisjoint schs = do
368369runProcessM :: Traced (Definitions Schema) -> ProcessM a -> (a, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
369370runProcessM defs = runWriter . (`runReaderT` defs) . runMemo ()
370371
371−schemaToFormula ::
372− Traced (Definitions Schema) ->
373− Traced Schema ->
374− (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
372+schemaToFormula
373+ :: Traced (Definitions Schema)
374+ -> Traced Schema
375+ -> (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
375376schemaToFormula defs rs = runProcessM defs $ processSchema rs
src/Data/OpenApi/Compare/Validate/Schema/Traced.hs → src/OpenAPI/Checker/Validate/Schema/Traced.hsrenamed+14−14
@@ -1,18 +1,18 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.Schema.Traced
4− ( Step (..),
5− tracedAllOf,
6− tracedAnyOf,
7− tracedOneOf,
8− tracedItems,
9− tracedAdditionalProperties,
10− tracedDiscriminator,
11− tracedProperties,
12− tracedConjunct,
13− PartitionLocation (..),
14− PartitionChoice (..),
15− Partition,
3+module OpenAPI.Checker.Validate.Schema.Traced
4+ ( Step (..)
5+ , tracedAllOf
6+ , tracedAnyOf
7+ , tracedOneOf
8+ , tracedItems
9+ , tracedAdditionalProperties
10+ , tracedDiscriminator
11+ , tracedProperties
12+ , tracedConjunct
13+ , PartitionLocation (..)
14+ , PartitionChoice (..)
15+ , Partition
1616 )
1717where
1818
@@ -21,9 +21,9 @@ import Data.Functor
2121import qualified Data.HashMap.Strict.InsOrd as IOHM
2222import qualified Data.List.NonEmpty as NE
2323import Data.OpenApi
24−import Data.OpenApi.Compare.Subtree
2524import qualified Data.Set as S
2625import Data.Text (Text)
26+import OpenAPI.Checker.Subtree
2727
2828data PartitionChoice
2929 = CByEnumValue (S.Set A.Value)
src/Data/OpenApi/Compare/Validate/Schema/TypedJson.hs → src/OpenAPI/Checker/Validate/Schema/TypedJson.hsrenamed+24−26
@@ -1,11 +1,11 @@
1−module Data.OpenApi.Compare.Validate.Schema.TypedJson
2− ( JsonType (..),
3− describeJSONType,
4− TypedValue (..),
5− untypeValue,
6− ForeachType (..),
7− foldType,
8− forType_,
1+module OpenAPI.Checker.Validate.Schema.TypedJson
2+ ( JsonType (..)
3+ , describeJSONType
4+ , TypedValue (..)
5+ , untypeValue
6+ , ForeachType (..)
7+ , foldType
8+ , forType_
99 )
1010where
1111
@@ -88,26 +88,24 @@ forType_ :: Applicative m => (forall x. Typeable x => JsonType -> (ForeachType f
8888forType_ k = getAp $ foldType (\ty proj -> Ap $ k ty proj)
8989
9090broadcastType :: (forall x. Typeable x => f x) -> ForeachType f
91−broadcastType k =
92− ForeachType
93− { forNull = k
94− , forBoolean = k
95− , forNumber = k
96− , forString = k
97− , forArray = k
98− , forObject = k
99− }
91+broadcastType k = ForeachType
92+ { forNull = k
93+ , forBoolean = k
94+ , forNumber = k
95+ , forString = k
96+ , forArray = k
97+ , forObject = k
98+ }
10099
101100zipType :: (forall x. Typeable x => f x -> g x -> h x) -> ForeachType f -> ForeachType g -> ForeachType h
102−zipType k f1 f2 =
103− ForeachType
104− { forNull = k (forNull f1) (forNull f2)
105− , forBoolean = k (forBoolean f1) (forBoolean f2)
106− , forNumber = k (forNumber f1) (forNumber f2)
107− , forString = k (forString f1) (forString f2)
108− , forArray = k (forArray f1) (forArray f2)
109− , forObject = k (forObject f1) (forObject f2)
110− }
101+zipType k f1 f2 = ForeachType
102+ { forNull = k (forNull f1) (forNull f2)
103+ , forBoolean = k (forBoolean f1) (forBoolean f2)
104+ , forNumber = k (forNumber f1) (forNumber f2)
105+ , forString = k (forString f1) (forString f2)
106+ , forArray = k (forArray f1) (forArray f2)
107+ , forObject = k (forObject f1) (forObject f2)
108+ }
111109
112110instance (forall x. Lattice (f x)) => Lattice (ForeachType f) where
113111 (\/) = zipType (\/)
src/Data/OpenApi/Compare/Validate/SecurityRequirement.hs → src/OpenAPI/Checker/Validate/SecurityRequirement.hsrenamed+13−14
@@ -1,8 +1,8 @@
11{-# LANGUAGE PartialTypeSignatures #-}
22{-# OPTIONS_GHC -Wno-orphans #-}
33
4−module Data.OpenApi.Compare.Validate.SecurityRequirement
5− ( Issue (..),
4+module OpenAPI.Checker.Validate.SecurityRequirement
5+ ( Issue (..)
66 )
77where
88
@@ -17,14 +17,14 @@ import Data.HList
1717import qualified Data.HashMap.Strict.InsOrd as IOHM
1818import qualified Data.List.NonEmpty as NE
1919import Data.OpenApi
20−import Data.OpenApi.Compare.Behavior
21−import Data.OpenApi.Compare.Subtree
22−import Data.OpenApi.Compare.Validate.OAuth2Flows
23−import Data.OpenApi.Compare.Validate.SecurityScheme ()
2420import Data.Set (Set)
2521import qualified Data.Set as S
2622import Data.Text (Text)
2723import Data.Traversable
24+import OpenAPI.Checker.Behavior
25+import OpenAPI.Checker.Subtree
26+import OpenAPI.Checker.Validate.OAuth2Flows
27+import OpenAPI.Checker.Validate.SecurityScheme ()
2828
2929instance Subtree SecurityRequirement where
3030 type SubtreeLevel SecurityRequirement = 'SecurityRequirementLevel
@@ -41,19 +41,18 @@ instance Subtree SecurityRequirement where
4141 for (IOHM.toList $ getSecurityRequirement sec) $ \(key, scopes) ->
4242 (,scopes) <$> lookupScheme key defs
4343 structuralMaybeWith
44− ( \pc' -> do
45− let ProdCons pScopes cScopes = fmap snd <$> pc'
46− unless (pScopes == cScopes) structuralIssue
47− structuralList env $ fmap fst <$> pc'
48− pure ()
49− )
44+ (\pc' -> do
45+ let ProdCons pScopes cScopes = fmap snd <$> pc'
46+ unless (pScopes == cScopes) structuralIssue
47+ structuralList env $ fmap fst <$> pc'
48+ pure ())
5049 normalized
5150 pure ()
5251 checkSemanticCompatibility env bhv' pc = do
5352 let schemes = getH @(ProdCons (Traced (Definitions SecurityScheme))) env
5453 ( ProdCons pErrs cErrs
55− , (ProdCons pSchemes cSchemes) ::
56− ProdCons [(Behavior 'SecuritySchemeLevel, Traced SecurityScheme, [Text])]
54+ , (ProdCons pSchemes cSchemes)
55+ :: ProdCons [(Behavior 'SecuritySchemeLevel, Traced SecurityScheme, [Text])]
5756 ) =
5857 NE.unzip $
5958 partitionEithers <$> do
src/Data/OpenApi/Compare/Validate/SecurityScheme.hs → src/OpenAPI/Checker/Validate/SecurityScheme.hsrenamed+5−5
@@ -1,16 +1,16 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.SecurityScheme
3+module OpenAPI.Checker.Validate.SecurityScheme
44 (
55 )
66where
77
88import Control.Monad
99import Data.OpenApi
10−import Data.OpenApi.Compare.Behavior
11−import Data.OpenApi.Compare.Orphans ()
12−import Data.OpenApi.Compare.Subtree
13−import Data.OpenApi.Compare.Validate.OAuth2Flows
10+import OpenAPI.Checker.Behavior
11+import OpenAPI.Checker.Orphans ()
12+import OpenAPI.Checker.Subtree
13+import OpenAPI.Checker.Validate.OAuth2Flows
1414
1515instance Subtree SecurityScheme where
1616 type CheckEnv SecurityScheme = '[]
src/Data/OpenApi/Compare/Validate/Server.hs → src/OpenAPI/Checker/Validate/Server.hsrenamed+18−21
@@ -1,7 +1,7 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
22
3−module Data.OpenApi.Compare.Validate.Server
4− ( Issue (..),
3+module OpenAPI.Checker.Validate.Server
4+ ( Issue (..)
55 )
66where
77
@@ -19,21 +19,21 @@ import qualified Data.HashSet.InsOrd as IOHM
1919import qualified Data.HashSet.InsOrd as IOHS
2020import Data.Maybe
2121import Data.OpenApi
22−import Data.OpenApi.Compare.Behavior
23−import Data.OpenApi.Compare.Common
24−import Data.OpenApi.Compare.Paths
25−import Data.OpenApi.Compare.Subtree
26−import Data.OpenApi.Compare.Validate.MediaTypeObject
2722import qualified Data.Set as S
2823import Data.Text (Text)
2924import qualified Data.Text as T
3025import Data.Traversable
26+import OpenAPI.Checker.Behavior
27+import OpenAPI.Checker.Common
28+import OpenAPI.Checker.Paths
29+import OpenAPI.Checker.Subtree
30+import OpenAPI.Checker.Validate.MediaTypeObject
3131import Text.Pandoc.Builder
3232import Prelude as P
3333
34−tracedParsedServerUrlParts ::
35− Server ->
36− Either (Issue 'ServerLevel) ProcessedServer
34+tracedParsedServerUrlParts
35+ :: Server
36+ -> Either (Issue 'ServerLevel) ProcessedServer
3737tracedParsedServerUrlParts s =
3838 let parsedUrl = parseServerUrl $ _serverUrl s
3939 lookupVar var = case IOHM.lookup var (_serverVariables s) of
@@ -46,7 +46,7 @@ instance Behavable 'OperationLevel 'ServerLevel where
4646 = InServer Text
4747 deriving stock (Eq, Ord, Show)
4848
49− describeBehavior (InServer n) = "Server " <> code n
49+ describeBehaviour (InServer n) = "Server " <> code n
5050
5151instance Subtree [Server] where
5252 type SubtreeLevel [Server] = 'OperationLevel
@@ -63,12 +63,11 @@ instance Subtree [Server] where
6363 pcServer
6464 <&> partitionEithers
6565 . fmap
66− ( \(Traced t s) ->
67− let bhv = beh >>> step (InServer $ _serverUrl s)
68− in case tracedParsedServerUrlParts s of
69− Left e -> Left $ issueAt bhv e
70− Right u -> Right (bhv, Traced (t >>> step (ServerStep $ _serverUrl s)) u)
71− )
66+ (\(Traced t s) ->
67+ let bhv = beh >>> step (InServer $ _serverUrl s)
68+ in case tracedParsedServerUrlParts s of
69+ Left e -> Left $ issueAt bhv e
70+ Right u -> Right (bhv, Traced (t >>> step (ServerStep $ _serverUrl s)) u))
7271 . sequence
7372 sequenceA_ pErrs
7473 sequenceA_ cErrs
@@ -102,7 +101,7 @@ data ServerUrlPart var
102101 deriving stock (Eq, Show, Functor, Foldable, Traversable)
103102
104103-- | This is super rough. Things like @{a|b}c@ will not match @ac@.
105−-- FIXME: #46
104+-- FIXME: https://github.com/typeable/openapi-diff/issues/46
106105--
107106-- NOTE: syntax is defined vaguely in the spec.
108107parseServerUrl :: Text -> [ServerUrlPart Text]
@@ -134,9 +133,7 @@ instance Issuable 'ServerLevel where
134133 | ServerVariableNotDefined Text
135134 | ServerNotMatched
136135 deriving stock (Eq, Ord, Show)
137− issueKind = \case
138− ServerVariableNotDefined _ -> SchemaInvalid
139− _ -> CertainIssue
136+ issueIsUnsupported _ = False
140137 describeIssue Forward (EnumValueNotConsumed _ v) =
141138 para $ "Enum value " <> code v <> " has been removed."
142139 describeIssue Backward (EnumValueNotConsumed _ v) =
src/OpenAPI/Checker/Validate/Sums.hsnew file+24
@@ -0,0 +1,24 @@
1+module OpenAPI.Checker.Validate.Sums
2+ ( checkSums
3+ ) where
4+
5+import Data.Foldable
6+import Data.Map.Strict (Map)
7+import qualified Data.Map.Strict as M
8+import OpenAPI.Checker.Behavior
9+import OpenAPI.Checker.Subtree
10+import OpenAPI.Checker.Paths
11+
12+checkSums
13+ :: (Ord k, Issuable l)
14+ => Paths q r l
15+ -> (k -> Issue l)
16+ -> (k -> ProdCons t -> CompatFormula' q AnIssue r ())
17+ -> ProdCons (Map k t)
18+ -> CompatFormula' q AnIssue r ()
19+checkSums xs noElt check (ProdCons p c) = for_ (M.toList p) $ \(key, prodElt) ->
20+ case M.lookup key c of
21+ Nothing -> issueAt xs $ noElt key
22+ Just consElt ->
23+ let sumElts = ProdCons prodElt consElt
24+ in check key sumElts
stack.yaml+3−18
@@ -1,23 +1,8 @@
1−resolver: lts-22.34
2−
3−packages:
4− - .
1+resolver: lts-18.2
52
63extra-deps:
7− - base16-bytestring-1.0.2.0
8− - freer-simple-1.2.1.2
9− - generics-sop-0.5.1.4
10− - github-0.29
11− - http-link-header-1.2.1
124 - open-union-0.4.0.0
13− - th-abstraction-0.7.0.0
145 - type-fun-0.1.3
15− - typerep-map-0.6.0.0
16−
17−# Required for freer-simple and typerep-map
18−allow-newer: true
196
20−nix:
21−# packages: [pkg-config yq zlib]
22− shell-file: ./nix/shell.nix
23− pure: false
7+apply-ghc-options: everything
8+rebuild-ghc-options: true
stack.yaml.lock+4−25
@@ -18,30 +18,9 @@ packages:
1818 sha256: b977d42525f0b4223959918d18e7905085283e937493c395077b608cd19b42c1
1919 original:
2020 hackage: type-fun-0.1.3
21−- completed:
22− hackage: github-0.26@sha256:a9d4046325c3eb28cdc7bef2c3f5bb213328caeae0b7dce6f51de655f0bffaa1,7162
23− pantry-tree:
24− size: 7511
25− sha256: b71aab2984b268030c9e2617043575681134c1fe60dffbd5596e659c0a3e9aec
26− original:
27− hackage: github-0.26
28−- completed:
29− hackage: base16-bytestring-0.1.1.7@sha256:0021256a9628971c08da95cb8f4d0d72192f3bb8a7b30b55c080562d17c43dd3,2231
30− pantry-tree:
31− size: 454
32− sha256: 2dc99cd67293b09204098202d0bdcdcf89d2b8c69ce6c8f4e31deadbdab2a7f9
33− original:
34− hackage: base16-bytestring-0.1.1.7
35−- completed:
36− hackage: http-link-header-1.0.3.1@sha256:9917c26635a46eff5d36c5344a4ae42fad2b5de79c0e300363bddd5eec66facd,1937
37− pantry-tree:
38− size: 990
39− sha256: 155feaf856daf5b55491048c8c4726ba765a8fe79263ebdbfc82f6c385a15715
40− original:
41− hackage: http-link-header-1.0.3.1
4221snapshots:
4322- completed:
44− size: 587393
45− url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/25.yaml
46− sha256: 1b74fb5e970497b5aefae56703f1bd44aa648bd1a5ef95c1eb8c29775087e2bf
47− original: lts-18.25
23+ size: 585392
24+ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/2.yaml
25+ sha256: 7abb45c0cc5eb349448b66d8753655542d45d387ad26970419282eab3d860724
26+ original: lts-18.2
test/README.md+1−1
@@ -30,7 +30,7 @@ This test would read the files `a.yaml` and `b.yaml` using the `Yaml.decodeFileT
3030
3131## Supported feature tests
3232
33−If a test starts with either `x ` or `v `, the test is assumed to test the support of some OpenAPI functionality. If the test begins `x ` it means that the feature is expected to be not supported, and the result will only be checked to have an "unsupported" flag set. If it starts with `v ` the test is expected to be supported and the result is compared to some file on disk.
33+If a test starts with either `x ` or `v `, the test is assumed to test the support of some OpenApi functionality. If the test begins `x ` it means that the feature is expected to be not supported, and the result will only be checked to have an "unsupported" flag set. If it starts with `v ` the test is expected to be supported and the result is compared to some file on disk.
3434
3535The result will be reflected in the generated compatibility matrix.
3636
test/Spec/Golden/Extra.hs+44−48
@@ -1,8 +1,8 @@
11module Spec.Golden.Extra
2− ( getGoldenInputs,
3− getGoldenInputsUniform,
4− goldenInputsTree,
5− goldenInputsTreeUniform,
2+ ( getGoldenInputs
3+ , getGoldenInputsUniform
4+ , goldenInputsTree
5+ , goldenInputsTreeUniform
66 )
77where
88
@@ -19,12 +19,12 @@ data TestInput t
1919 | TestInputLeaf TestName t FilePath
2020 deriving stock (Functor)
2121
22−getGoldenInputs ::
23− (Each s t (FilePath, FilePath -> IO a) a) =>
24− TestName ->
25− FilePath ->
26− s ->
27− IO (TestInput t)
22+getGoldenInputs
23+ :: (Each s t (FilePath, FilePath -> IO a) a)
24+ => TestName
25+ -> FilePath
26+ -> s
27+ -> IO (TestInput t)
2828getGoldenInputs name filepath inp = do
2929 dirs' <- listDirectory filepath >>= filterM (doesDirectoryExist . (filepath </>))
3030 case dirs' of
@@ -39,55 +39,51 @@ getGoldenInputs name filepath inp = do
3939 TestInputNode name
4040 <$> forM dirs (\dir -> getGoldenInputs dir (filepath </> dir) inp)
4141
42−getGoldenInputsUniform ::
43− (Each t h (FilePath, FilePath -> IO a) a) =>
44− (Each s t FilePath (FilePath, FilePath -> IO a)) =>
45− TestName ->
46− (FilePath -> IO a) ->
47− FilePath ->
48− s ->
49− IO (TestInput h)
42+getGoldenInputsUniform
43+ :: (Each t h (FilePath, FilePath -> IO a) a)
44+ => (Each s t FilePath (FilePath, FilePath -> IO a))
45+ => TestName
46+ -> (FilePath -> IO a)
47+ -> FilePath
48+ -> s
49+ -> IO (TestInput h)
5050getGoldenInputsUniform name f filepath inp = getGoldenInputs name filepath $ inp & each %~ (,f)
5151
52−goldenInputsTree ::
53− (Each s t (FilePath, FilePath -> IO a) a) =>
54− TestName ->
55− -- | Root path
56− FilePath ->
57− -- | Name of golden file
58− FilePath ->
59− s ->
60− (t -> IO BSL.ByteString) ->
61− IO TestTree
62−goldenInputsTree name filepath golden inp f =
52+goldenInputsTree
53+ :: (Each s t (FilePath, FilePath -> IO a) a)
54+ => TestName
55+ -> FilePath -- ^ Root path
56+ -> FilePath -- ^ Name of golden file
57+ -> s
58+ -> (t -> IO BSL.ByteString)
59+ -> IO TestTree
60+goldenInputsTree name filepath golden inp f = do
6361 runTestInputTree golden f <$> getGoldenInputs name filepath inp
6462
65−runTestInputTree ::
66− FilePath ->
67− (t -> IO BSL.ByteString) ->
68− TestInput t ->
69− TestTree
63+runTestInputTree
64+ :: FilePath
65+ -> (t -> IO BSL.ByteString)
66+ -> TestInput t
67+ -> TestTree
7068runTestInputTree golden f (TestInputNode name rest) =
7169 testGroup name (runTestInputTree golden f <$> rest)
7270runTestInputTree golden f (TestInputLeaf name t path) =
7371 goldenVsStringDiff
7472 name
75− (\ref new -> ["diff-yaml", ref, new])
73+ (\ref new -> ["diff", "-u", ref, new])
7674 (path </> golden)
7775 (f t)
7876
79−goldenInputsTreeUniform ::
80− ( Each t h (FilePath, FilePath -> IO a) a
81− , Each s t FilePath (FilePath, FilePath -> IO a)
82− ) =>
83− String ->
84− -- | Root path
85− FilePath ->
86− -- | Name of golden file
87− FilePath ->
88− s ->
89− (FilePath -> IO a) ->
90− (h -> IO BSL.ByteString) ->
91− IO TestTree
77+goldenInputsTreeUniform
78+ :: ( Each t h (FilePath, FilePath -> IO a) a
79+ , Each s t FilePath (FilePath, FilePath -> IO a)
80+ )
81+ => String
82+ -> FilePath -- ^ Root path
83+ -> FilePath -- ^ Name of golden file
84+ -> s
85+ -> (FilePath -> IO a)
86+ -> (h -> IO BSL.ByteString)
87+ -> IO TestTree
9288goldenInputsTreeUniform name filepath golden inp h =
9389 goldenInputsTree name filepath golden (inp & each %~ (,h))
test/Spec/Golden/TraceTree.hs+6−7
@@ -1,5 +1,5 @@
11module Spec.Golden.TraceTree
2− ( tests,
2+ ( tests
33 )
44where
55
@@ -7,14 +7,13 @@ import Control.Category
77import Control.Exception
88import qualified Data.ByteString.Lazy as BSL
99import Data.Default
10−import Data.OpenApi.Compare.Run
11−import Data.OpenApi.Compare.Validate.OpenApi ()
1210import Data.Text (Text)
1311import qualified Data.Text.Encoding as T
1412import qualified Data.Yaml as Yaml
13+import OpenAPI.Checker.Run
14+import OpenAPI.Checker.Validate.OpenApi ()
1515import Spec.Golden.Extra
1616import Test.Tasty (TestTree, testGroup)
17−import Text.Pandoc.Builder
1817import Text.Pandoc.Class
1918import Text.Pandoc.Options
2019import Text.Pandoc.Writers
@@ -25,7 +24,7 @@ tests = do
2524 traceTreeTests <-
2625 goldenInputsTreeUniform
2726 "TraceTree"
28− "test/golden"
27+ "test/golden/common"
2928 "trace-tree.yaml"
3029 ("a.yaml", "b.yaml")
3130 Yaml.decodeFileThrow
@@ -34,11 +33,11 @@ tests = do
3433 reportTests <-
3534 goldenInputsTreeUniform
3635 "Report"
37− "test/golden"
36+ "test/golden/common"
3837 "report.md"
3938 ("a.yaml", "b.yaml")
4039 Yaml.decodeFileThrow
41− (runPandoc . writeMarkdown def {writerExtensions = githubMarkdownExtensions} . doc . fst . runReport def)
40+ (runPandoc . writeMarkdown def {writerExtensions = githubMarkdownExtensions} . fst . runReport def)
4241
4342 return $ testGroup "Golden tests" [traceTreeTests, reportTests]
4443
test/golden/common/bad-oneof-unchanged/report.md+6−6
@@ -1,19 +1,19 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes | [❓ Unsupported feature changes](#unsupported-changes) |
4−|---------------------|-------------------------|--------------------------------------------------------|
5−| 0 | 0 | 2 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | [🤷 Unsupported feature changes](#unsupported-changes) |
4+|---------------------|------------------------|-------------------------------------------------------|
5+| 0 | 0 | 2 |
66
7−# <span id="unsupported-changes"></span>❓ Unsupported feature changes
7+# <span id="unsupported-changes"></span>🤷 Unsupported feature changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313Could not deduce that `oneOf` cases don't overlap. Treating the `oneOf`
1414as an `anyOf`. Reported errors might not be accurate.
1515
16−### ⬅️☁️ JSON Response – 200
16+### 📱⬅️ JSON Response – 200
1717
1818Could not deduce that `oneOf` cases don't overlap. Treating the `oneOf`
1919as an `anyOf`. Reported errors might not be accurate.
test/golden/common/bad-oneof-unchanged/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -7,7 +7,7 @@ forwardChanges:
77 WithStatusCode 200:
88 ResponsePayload:
99 PayloadSchema: OneOfNotDisjoint
10−backwardChanges:
10+nonBreakingChanges:
1111 AtPath "/test":
1212 InOperation PostMethod:
1313 InRequest:
test/golden/common/enum-added/report.md+7−7
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
1212
1313#### `$(String)`
1414
@@ -18,11 +18,11 @@ The following enum value was added:
1818"C"
1919```
2020
21−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
21+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2222
2323## **POST** /test
2424
25−### ➡️☁️ JSON Request
25+### 📱➡️ JSON Request
2626
2727#### `$(String)`
2828
test/golden/common/enum-added/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 WithStatusCode 200:
55 ResponsePayload:
66 PayloadSchema:
77 OfType String: EnumDoesntSatisfy (String "C")
8−backwardChanges:
8+nonBreakingChanges:
99 AtPath "/test":
1010 InOperation PostMethod:
1111 InRequest:
test/golden/common/enum-anyof/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/enum-anyof/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/id/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/id/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/json/recursive/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/json/recursive/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/maximum-lowered/report.md+7−7
@@ -1,24 +1,24 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$(Number)`
1414
1515Upper bound changed from 3.0 inclusive to 2.0 inclusive.
1616
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
1818
1919## **POST** /test
2020
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
2222
2323#### `$(Number)`
2424
test/golden/common/maximum-lowered/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -6,7 +6,7 @@ forwardChanges:
66 PayloadSchema:
77 OfType Number: MatchingMaximumWeak (ProdCons {producer = Inclusive 3.0,
88 consumer = Inclusive 2.0})
9−backwardChanges:
9+nonBreakingChanges:
1010 AtPath "/test":
1111 InOperation PostMethod:
1212 WithStatusCode 200:
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InParam "test1": ParamEmptinessIncompatible
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod:
55 InParam "test1": ParamEmptinessIncompatible
test/golden/common/pathItem/operation/parameters/change/report.md+5−5
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
@@ -16,7 +16,7 @@
1616
1717The type has been removed.
1818
19−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
19+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2020
2121## **POST** /test
2222
test/golden/common/pathItem/operation/parameters/change/trace-tree.yaml+2−2
@@ -1,10 +1,10 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InParam "test":
55 InParamSchema:
66 OfType String: TypeBecomesEmpty
7−backwardChanges:
7+nonBreakingChanges:
88 AtPath "/test":
99 InOperation PostMethod:
1010 InParam "test":
test/golden/common/pathItem/operation/parameters/required/false/add/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/pathItem/operation/parameters/required/false/add/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/false/del/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/pathItem/operation/parameters/required/false/del/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/reset/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/parameters/required/reset/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod:
55 InParam "test1": ParamRequired
test/golden/common/pathItem/operation/parameters/required/set/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/parameters/required/set/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InParam "test1": ParamRequired
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/true/add/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/parameters/required/true/add/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod: ParamNotMatched "test2"
4−backwardChanges: {}
4+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/true/del/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/parameters/required/true/del/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod: ParamNotMatched "test2"
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod:
55 InRequest: RequestMediaTypeNotFound application/x-www-form-urlencoded
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/report.md+7−7
@@ -1,24 +1,24 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$(String)`
1414
1515The type has been removed.
1616
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
1818
1919## **POST** /test
2020
21−### ➡️☁️ JSON Request
21+### 📱➡️ JSON Request
2222
2323#### `$(Number)`
2424
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
55 InPayload:
66 PayloadSchema:
77 OfType String: TypeBecomesEmpty
8−backwardChanges:
8+nonBreakingChanges:
99 AtPath "/test":
1010 InOperation PostMethod:
1111 InRequest:
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest: RequestMediaTypeNotFound application/x-www-form-urlencoded
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/requestBody/required/reset/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/requestBody/required/reset/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod:
55 InRequest: RequestBodyRequired
test/golden/common/pathItem/operation/requestBody/required/set/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/requestBody/required/set/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest: RequestBodyRequired
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/add/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/responses/add/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
4−backwardChanges: {}
4+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod:
55 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/add/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/pathItem/operation/responses/change/headers/optional/add/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/del/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/pathItem/operation/responses/change/headers/optional/del/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 1 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|------------------------------------------|------------------------|-------------------------------|
5+| 1 | 0 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 WithStatusCode 200: ConsumerDoesntHaveMediaType application/x-www-form-urlencoded
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/report.md+7−7
@@ -1,24 +1,24 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
1212
1313#### `$(Number)`
1414
1515The type has been added.
1616
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
1818
1919## **POST** /test
2020
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
2222
2323#### `$(String)`
2424
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 WithStatusCode 200:
55 ResponsePayload:
66 PayloadSchema:
77 OfType Number: TypeBecomesEmpty
8−backwardChanges:
8+nonBreakingChanges:
99 AtPath "/test":
1010 InOperation PostMethod:
1111 WithStatusCode 200:
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod:
55 WithStatusCode 200: ConsumerDoesntHaveMediaType application/x-www-form-urlencoded
test/golden/common/pathItem/operation/responses/del/report.md+4−4
@@ -1,10 +1,10 @@
11# Summary
22
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|---------------------|--------------------------------------------------|
5−| 0 | 1 |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|---------------------|-------------------------------------------------|-------------------------------|
5+| 0 | 1 | 0 |
66
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
88
99## **POST** /test
1010
test/golden/common/pathItem/operation/responses/del/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges: {}
2−backwardChanges:
1+breakingChanges: {}
2+nonBreakingChanges:
33 AtPath "/test":
44 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
test/golden/common/property-removed-additional/report.md+7−7
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 3 | 3 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 3 | 3 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
1212
1313#### `$.property2`
1414
@@ -21,11 +21,11 @@
2121
22223. The property may not be present.
2323
24−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
24+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2525
2626## **POST** /test
2727
28−### ➡️☁️ JSON Request
28+### 📱➡️ JSON Request
2929
3030#### `$.property2`
3131
test/golden/common/property-removed-additional/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 WithStatusCode 200:
@@ -9,7 +9,7 @@ forwardChanges:
99 - TypesRestricted [Number]
1010 - AdditionalToProperty
1111 - PropertyNowRequired
12−backwardChanges:
12+nonBreakingChanges:
1313 AtPath "/test":
1414 InOperation PostMethod:
1515 InRequest:
test/golden/common/property-removed/report.md+22−6
@@ -1,21 +1,37 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 2 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 2 | 2 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$.property2`
1414
1515The property has been removed.
1616
17−### ⬅️☁️ JSON Response – 200
17+### 📱⬅️ JSON Response – 200
1818
1919#### `$.property2`
2020
2121The property may not be present.
22+
23+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
24+
25+## **POST** /test
26+
27+### 📱➡️ JSON Request
28+
29+#### `$.property2`
30+
31+The property may not be present.
32+
33+### 📱⬅️ JSON Response – 200
34+
35+#### `$.property2`
36+
37+The property has been removed.
test/golden/common/property-removed/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -11,7 +11,7 @@ forwardChanges:
1111 PayloadSchema:
1212 OfType Object:
1313 InProperty "property2": PropertyNowRequired
14−backwardChanges:
14+nonBreakingChanges:
1515 AtPath "/test":
1616 InOperation PostMethod:
1717 InRequest:
test/golden/common/property-required/report.md+7−7
@@ -1,24 +1,24 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$.property2`
1414
1515The property has become required.
1616
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
1818
1919## **POST** /test
2020
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
2222
2323#### `$.property2`
2424
test/golden/common/property-required/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -6,7 +6,7 @@ forwardChanges:
66 PayloadSchema:
77 OfType Object:
88 InProperty "property2": PropertyNowRequired
9−backwardChanges:
9+nonBreakingChanges:
1010 AtPath "/test":
1111 InOperation PostMethod:
1212 WithStatusCode 200:
test/golden/common/random-example/a.yamldeleted−56
@@ -1,56 +0,0 @@
1−openapi: "3.0.0"
2−info:
3− version: 1.0.0
4− title: Swagger Petstore
5− license:
6− name: MIT
7−servers:
8− - url: https://example.com
9−paths:
10− /pets:
11− get:
12− parameters:
13− - name: limit
14− in: query
15− required: false
16− schema:
17− type: integer
18− maximum: 20
19− responses:
20− "200":
21− description: ""
22− headers:
23− x-next:
24− schema:
25− type: string
26− content:
27− application/json:
28− schema:
29− $ref: "#/components/schemas/Pets"
30− post:
31− requestBody:
32− content:
33− application/json:
34− schema:
35− $ref: "#/components/schemas/Pet"
36− responses:
37− "201":
38− description: ""
39−components:
40− schemas:
41− Pet:
42− type: object
43− required:
44− - id
45− - name
46− properties:
47− id:
48− type: integer
49− name:
50− type: string
51− minLength: 3
52− maxLength: 10
53− Pets:
54− type: array
55− items:
56− $ref: "#/components/schemas/Pet"
test/golden/common/random-example/b.yamldeleted−58
@@ -1,58 +0,0 @@
1−openapi: "3.0.0"
2−info:
3− version: 1.1.0
4− title: Swagger Petstore
5− license:
6− name: MIT
7−servers:
8− - url: https://example.com
9−paths:
10− /pets:
11− get:
12− parameters:
13− - name: limit
14− in: query
15− required: false
16− schema:
17− type: integer
18− maximum: 30
19− responses:
20− "200":
21− description: ""
22− headers:
23− x-next:
24− schema:
25− type: string
26− content:
27− application/json:
28− schema:
29− $ref: "#/components/schemas/Pets"
30− post:
31− requestBody:
32− content:
33− application/json:
34− schema:
35− $ref: "#/components/schemas/Pet"
36− responses:
37− "201":
38− description: ""
39−components:
40− schemas:
41− Pet:
42− type: object
43− required:
44− - id
45− - name
46− properties:
47− id:
48− type: integer
49− name:
50− type: string
51− minLength: 1
52− maxLength: 15
53− weight:
54− type: integer
55− Pets:
56− type: array
57− items:
58− $ref: "#/components/schemas/Pet"
test/golden/common/random-example/report.mddeleted−71
@@ -1,71 +0,0 @@
1−# Summary
2−
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 5 | 6 |
6−
7−# <span id="breaking-changes"></span>❌ Breaking changes
8−
9−## **GET** /pets
10−
11−### ⬅️☁️ JSON Response – 200
12−
13−#### `$[*].name(String)`
14−
15−1. Maximum length of the string changed from 10 to 15.
16−
17−2. Minimum length of the string changed from 3 to 1.
18−
19−## **POST** /pets
20−
21−### ➡️☁️ JSON Request
22−
23−#### `$.weight`
24−
25−1. Values are now limited to the following types:
26−
27− - Number
28−
29−2. The property was previously implicitly described by the catch-all
30− "additional properties" case. It is now explicitly defined.
31−
32−#### `$.weight(Number)`
33−
34−Value is now a multiple of 1.0.
35−
36−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
37−
38−## **GET** /pets
39−
40−### Parameter limit
41−
42−#### JSON Schema
43−
44−##### `$(Number)`
45−
46−Upper bound changed from 20.0 inclusive to 30.0 inclusive.
47−
48−### ⬅️☁️ JSON Response – 200
49−
50−#### `$[*].weight`
51−
52−1. Values are now limited to the following types:
53−
54− - Number
55−
56−2. The property was previously implicitly described by the catch-all
57− "additional properties" case. It is now explicitly defined.
58−
59−#### `$[*].weight(Number)`
60−
61−Value is now a multiple of 1.0.
62−
63−## **POST** /pets
64−
65−### ➡️☁️ JSON Request
66−
67−#### `$.name(String)`
68−
69−1. Maximum length of the string changed from 10 to 15.
70−
71−2. Minimum length of the string changed from 3 to 1.
test/golden/common/random-example/trace-tree.yamldeleted−48
@@ -1,48 +0,0 @@
1−forwardChanges:
2− AtPath "/pets":
3− InOperation GetMethod:
4− WithStatusCode 200:
5− ResponsePayload:
6− PayloadSchema:
7− OfType Array:
8− InItems:
9− OfType Object:
10− InProperty "name":
11− OfType String:
12− - MatchingMaxLengthWeak (ProdCons {producer = 15, consumer = 10})
13− - MatchingMinLengthWeak (ProdCons {producer = 1, consumer = 3})
14− InOperation PostMethod:
15− InRequest:
16− InPayload:
17− PayloadSchema:
18− OfType Object:
19− InProperty "weight":
20− - TypesRestricted [Number]
21− - AdditionalToProperty
22− - OfType Number: NoMatchingMultipleOf 1.0
23−backwardChanges:
24− AtPath "/pets":
25− InOperation GetMethod:
26− InParam "limit":
27− InParamSchema:
28− OfType Number: MatchingMaximumWeak (ProdCons {producer = Inclusive 30.0,
29− consumer = Inclusive 20.0})
30− WithStatusCode 200:
31− ResponsePayload:
32− PayloadSchema:
33− OfType Array:
34− InItems:
35− OfType Object:
36− InProperty "weight":
37− - TypesRestricted [Number]
38− - AdditionalToProperty
39− - OfType Number: NoMatchingMultipleOf 1.0
40− InOperation PostMethod:
41− InRequest:
42− InPayload:
43− PayloadSchema:
44− OfType Object:
45− InProperty "name":
46− OfType String:
47− - MatchingMaxLengthWeak (ProdCons {producer = 15, consumer = 10})
48− - MatchingMinLengthWeak (ProdCons {producer = 1, consumer = 3})
test/golden/common/recursive/report.md+7−7
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **GET** /api/foo
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$.leaf.value.foo(String)`
1414
@@ -18,11 +18,11 @@ The following enum value has been added:
1818"a"
1919```
2020
21−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
21+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2222
2323## **GET** /api/foo
2424
25−### ⬅️☁️ JSON Response – 200
25+### 📱⬅️ JSON Response – 200
2626
2727#### `$.leaf.value.foo(String)`
2828
test/golden/common/recursive/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/api/foo":
33 InOperation GetMethod:
44 InRequest:
@@ -11,7 +11,7 @@ forwardChanges:
1111 OfType Object:
1212 InProperty "foo":
1313 OfType String: NoMatchingEnum (String "a")
14−backwardChanges:
14+nonBreakingChanges:
1515 AtPath "/api/foo":
1616 InOperation GetMethod:
1717 WithStatusCode 200:
test/golden/common/security-scheme/report.md+5−5
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 2 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 2 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /oauth/check
1010
@@ -22,7 +22,7 @@ Security scheme has been removed.
2222
2323Security scheme has been removed.
2424
25−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
25+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2626
2727## **POST** /oauth/check
2828
test/golden/common/security-scheme/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/oauth/sign_out":
33 InOperation GetMethod:
44 SecurityRequirementStep 1:
@@ -7,7 +7,7 @@ forwardChanges:
77 InOperation PostMethod:
88 SecurityRequirementStep 0:
99 SecuritySchemeStep "oauth": SecuritySchemeNotMatched
10−backwardChanges:
10+nonBreakingChanges:
1111 AtPath "/oauth/check":
1212 InOperation PostMethod:
1313 SecurityRequirementStep 0:
test/golden/common/servers/report.md+5−5
@@ -1,10 +1,10 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 3 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 3 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **GET** /pets
1010
@@ -18,7 +18,7 @@ The server was removed.
1818
19192. A variable has been changed from being open to being closed.
2020
21−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
21+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2222
2323## **GET** /pets
2424
test/golden/common/servers/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/pets":
33 InOperation GetMethod:
44 InServer "http://{x}variable.path/{y}/{openVariable1}/{openVariable2}":
55 - EnumValueNotConsumed 1 "a"
66 - ConsumerNotOpen 7
77 InServer "http://missing.url": ServerNotMatched
8−backwardChanges:
8+nonBreakingChanges:
99 AtPath "/pets":
1010 InOperation GetMethod:
1111 InServer "http://{x}variable.path/{y}/{openVariable1}/{openVariable2}": EnumValueNotConsumed
test/golden/common/tuple-items/heterogeneous-component-mismatch/report.md+9−9
@@ -1,36 +1,36 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 2 | 2 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 2 | 2 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$[1](String)`
1414
1515The type has been removed.
1616
17−### ⬅️☁️ JSON Response – 200
17+### 📱⬅️ JSON Response – 200
1818
1919#### `$[1](Object)`
2020
2121The type has been added.
2222
23−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
23+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2424
2525## **POST** /test
2626
27−### ➡️☁️ JSON Request
27+### 📱➡️ JSON Request
2828
2929#### `$[1](Object)`
3030
3131The type has been added.
3232
33−### ⬅️☁️ JSON Response – 200
33+### 📱⬅️ JSON Response – 200
3434
3535#### `$[1](String)`
3636
test/golden/common/tuple-items/heterogeneous-component-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -13,7 +13,7 @@ forwardChanges:
1313 OfType Array:
1414 InItem 1:
1515 OfType Object: TypeBecomesEmpty
16−backwardChanges:
16+nonBreakingChanges:
1717 AtPath "/test":
1818 InOperation PostMethod:
1919 InRequest:
test/golden/common/tuple-items/heterogeneous-length-mismatch/report.md+22−6
@@ -1,20 +1,36 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
4−|------------------------------------------|-------------------------|
5−| 2 | 0 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 2 | 2 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$(Array)`
1414
1515Tuple length changed from 3 to 2.
1616
17−### ⬅️☁️ JSON Response – 200
17+### 📱⬅️ JSON Response – 200
18+
19+#### `$(Array)`
20+
21+Tuple length changed from 2 to 3.
22+
23+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
24+
25+## **POST** /test
26+
27+### 📱➡️ JSON Request
28+
29+#### `$(Array)`
30+
31+Tuple length changed from 2 to 3.
32+
33+### 📱⬅️ JSON Response – 200
1834
1935#### `$(Array)`
2036
test/golden/common/tuple-items/heterogeneous-length-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -11,7 +11,7 @@ forwardChanges:
1111 PayloadSchema:
1212 OfType Array: TupleItemsLengthChanged (ProdCons {producer = 2, consumer
1313 = 3})
14−backwardChanges:
14+nonBreakingChanges:
1515 AtPath "/test":
1616 InOperation PostMethod:
1717 InRequest:
test/golden/common/tuple-items/homogeneous-component-mismatch/report.md+17−9
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 4 | 2 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 4 | 4 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### `$(Array)`
1414
@@ -18,7 +18,7 @@ The array is no longer explicitly defined as a tuple.
1818
1919The type has been removed.
2020
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
2222
2323#### `$(Array)`
2424
@@ -28,17 +28,25 @@ The array is no longer explicitly defined as a tuple.
2828
2929The type has been added.
3030
31−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
31+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
3232
3333## **POST** /test
3434
35−### ➡️☁️ JSON Request
35+### 📱➡️ JSON Request
36+
37+#### `$(Array)`
38+
39+The array is no longer explicitly defined as a tuple.
3640
3741#### `$[0](Number)`
3842
3943The type has been added.
4044
41−### ⬅️☁️ JSON Response – 200
45+### 📱⬅️ JSON Response – 200
46+
47+#### `$(Array)`
48+
49+The array is no longer explicitly defined as a tuple.
4250
4351#### `$[0](String)`
4452
test/golden/common/tuple-items/homogeneous-component-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -15,7 +15,7 @@ forwardChanges:
1515 - ArrayToTuple
1616 - InItem 0:
1717 OfType Number: TypeBecomesEmpty
18−backwardChanges:
18+nonBreakingChanges:
1919 AtPath "/test":
2020 InOperation PostMethod:
2121 InRequest:
test/golden/common/tuple-items/homogeneous-length-mismatch/report.md+15−15
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 5 | 5 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 5 | 5 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test1
1010
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
1212
1313#### `$(Array)`
1414
@@ -16,7 +16,7 @@ The array is no longer explicitly defined as a tuple.
1616
1717## **POST** /test2
1818
19−### ⬅️☁️ JSON Response – 200
19+### 📱⬅️ JSON Response – 200
2020
2121#### `$(Array)`
2222
@@ -24,25 +24,25 @@ The array is no longer explicitly defined as a tuple.
2424
2525## **POST** /test3
2626
27−### ➡️☁️ JSON Request
27+### 📱➡️ JSON Request
2828
2929#### `$(Array)`
3030
3131Minimum length of the array changed from 2 to 3.
3232
33−### ⬅️☁️ JSON Response – 200
33+### 📱⬅️ JSON Response – 200
3434
3535#### `$(Array)`
3636
37−1. Tuple length changed from 2 to 3.
37+1. Tuple length changed from 3 to 2.
3838
39392. The array is no longer explicitly defined as a tuple.
4040
41−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
41+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
4242
4343## **POST** /test1
4444
45−### ➡️☁️ JSON Request
45+### 📱➡️ JSON Request
4646
4747#### `$(Array)`
4848
@@ -50,7 +50,7 @@ The array is no longer explicitly defined as a tuple.
5050
5151## **POST** /test2
5252
53−### ➡️☁️ JSON Request
53+### 📱➡️ JSON Request
5454
5555#### `$(Array)`
5656
@@ -58,15 +58,15 @@ The array is no longer explicitly defined as a tuple.
5858
5959## **POST** /test3
6060
61−### ➡️☁️ JSON Request
61+### 📱➡️ JSON Request
6262
6363#### `$(Array)`
6464
65−1. Tuple length changed from 2 to 3.
65+1. Tuple length changed from 3 to 2.
6666
67672. The array is no longer explicitly defined as a tuple.
6868
69−### ⬅️☁️ JSON Response – 200
69+### 📱⬅️ JSON Response – 200
7070
7171#### `$(Array)`
7272
test/golden/common/tuple-items/homogeneous-length-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test3":
33 InOperation PostMethod:
44 InRequest:
@@ -24,7 +24,7 @@ forwardChanges:
2424 ResponsePayload:
2525 PayloadSchema:
2626 OfType Array: NoMatchingTupleItems
27−backwardChanges:
27+nonBreakingChanges:
2828 AtPath "/test3":
2929 InOperation PostMethod:
3030 InRequest:
test/golden/common/tuple-items/homogeneous-vector/report.md+3−3
@@ -1,5 +1,5 @@
11# Summary
22
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
4−|---------------------|-------------------------|
5−| 0 | 0 |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4+|---------------------|------------------------|-------------------------------|
5+| 0 | 0 | 0 |
test/golden/common/tuple-items/homogeneous-vector/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
2−backwardChanges: {}
1+breakingChanges: {}
2+nonBreakingChanges: {}
test/golden/common/type-changing/report.md+15−15
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 6 | 6 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 6 | 6 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test1
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313Values are now limited to the following types:
1414
@@ -16,7 +16,7 @@ Values are now limited to the following types:
1616
1717## **POST** /test2
1818
19−### ➡️☁️ JSON Request
19+### 📱➡️ JSON Request
2020
2121Values are now limited to the following types:
2222
@@ -28,7 +28,7 @@ Values are now limited to the following types:
2828
2929## **POST** /test3
3030
31−### ➡️☁️ JSON Request
31+### 📱➡️ JSON Request
3232
3333#### `$(Array)`
3434
@@ -36,7 +36,7 @@ The type has been removed.
3636
3737## **POST** /test4
3838
39−### ➡️☁️ JSON Request
39+### 📱➡️ JSON Request
4040
4141#### `$(Object)`
4242
@@ -44,7 +44,7 @@ The type has been removed.
4444
4545## **POST** /test5
4646
47−### ➡️☁️ JSON Request
47+### 📱➡️ JSON Request
4848
4949Values are now limited to the following types:
5050
@@ -54,11 +54,11 @@ Values are now limited to the following types:
5454
5555Value is now a multiple of 1.0.
5656
57−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
57+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
5858
5959## **POST** /test1
6060
61−### ⬅️☁️ JSON Response – 200
61+### 📱⬅️ JSON Response – 200
6262
6363Values are now limited to the following types:
6464
@@ -66,7 +66,7 @@ Values are now limited to the following types:
6666
6767## **POST** /test2
6868
69−### ⬅️☁️ JSON Response – 200
69+### 📱⬅️ JSON Response – 200
7070
7171Values are now limited to the following types:
7272
@@ -78,7 +78,7 @@ Values are now limited to the following types:
7878
7979## **POST** /test3
8080
81−### ⬅️☁️ JSON Response – 200
81+### 📱⬅️ JSON Response – 200
8282
8383#### `$(Array)`
8484
@@ -86,7 +86,7 @@ The type has been removed.
8686
8787## **POST** /test4
8888
89−### ⬅️☁️ JSON Response – 200
89+### 📱⬅️ JSON Response – 200
9090
9191#### `$(Object)`
9292
@@ -94,7 +94,7 @@ The type has been removed.
9494
9595## **POST** /test5
9696
97−### ⬅️☁️ JSON Response – 200
97+### 📱⬅️ JSON Response – 200
9898
9999Values are now limited to the following types:
100100
test/golden/common/type-changing/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test5":
33 InOperation PostMethod:
44 InRequest:
@@ -28,7 +28,7 @@ forwardChanges:
2828 InRequest:
2929 InPayload:
3030 PayloadSchema: TypesRestricted [Number,Array,Object]
31−backwardChanges:
31+nonBreakingChanges:
3232 AtPath "/test5":
3333 InOperation PostMethod:
3434 WithStatusCode 200:
test/golden/common/unguarded-recursive/basic/a.yaml → test/golden/common/unguarded-recursive/a.yamlrenamed

The content is the same.

test/golden/common/unguarded-recursive/basic/b.yaml → test/golden/common/unguarded-recursive/b.yamlrenamed

The content is the same.

test/golden/common/unguarded-recursive/basic/report.mddeleted−13
@@ -1,13 +0,0 @@
1−# Summary
2−
3−| ❌ Breaking changes | ⚠️ Non-breaking changes | [❓ Unsupported feature changes](#unsupported-changes) |
4−|---------------------|-------------------------|--------------------------------------------------------|
5−| 0 | 0 | 1 |
6−
7−# <span id="unsupported-changes"></span>❓ Unsupported feature changes
8−
9−## **GET** /api/foo
10−
11−### ⬅️☁️ JSON Response – 200
12−
13−Encountered recursion that is too complex for CompaREST to untangle.
test/golden/common/unguarded-recursive/report.mdnew file+13
@@ -0,0 +1,13 @@
1+# Summary
2+
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | [🤷 Unsupported feature changes](#unsupported-changes) |
4+|---------------------|------------------------|-------------------------------------------------------|
5+| 0 | 0 | 1 |
6+
7+# <span id="unsupported-changes"></span>🤷 Unsupported feature changes
8+
9+## **GET** /api/foo
10+
11+### 📱⬅️ JSON Response – 200
12+
13+Encountered recursion that is too complex for OpenApi Diff to untangle.
test/golden/common/unguarded-recursive/silent/a.yamldeleted−25
@@ -1,25 +0,0 @@
1−components:
2− schemas:
3− A:
4− type: object
5− properties:
6− bar:
7− $ref: "#/components/schemas/B"
8− B:
9− anyOf:
10− - $ref: "#/components/schemas/B"
11− type: object
12−openapi: 3.0.0
13−info:
14− version: ""
15− title: ""
16−paths:
17− /foo:
18− get:
19− responses:
20− "200":
21− content:
22− application/json;charset=utf-8:
23− schema:
24− $ref: "#/components/schemas/A"
25− description: ""
test/golden/common/unguarded-recursive/silent/b.yamldeleted−19
@@ -1,19 +0,0 @@
1−components:
2− schemas: {}
3−openapi: 3.0.0
4−info:
5− version: ""
6− title: ""
7−paths:
8− /foo:
9− get:
10− responses:
11− "200":
12− content:
13− application/json;charset=utf-8:
14− schema:
15− type: object
16− properties:
17− bar:
18− type: object
19− description: ""
test/golden/common/unguarded-recursive/silent/report.mddeleted−15
@@ -1,15 +0,0 @@
1−# Summary
2−
3−| ❌ Breaking changes | ⚠️ Non-breaking changes | [❓ Unsupported feature changes](#unsupported-changes) |
4−|---------------------|-------------------------|--------------------------------------------------------|
5−| 0 | 0 | 1 |
6−
7−# <span id="unsupported-changes"></span>❓ Unsupported feature changes
8−
9−## **GET** /foo
10−
11−### ⬅️☁️ JSON Response – 200
12−
13−#### `$.bar`
14−
15−Encountered recursion that is too complex for CompaREST to untangle.
test/golden/common/unguarded-recursive/silent/trace-tree.yamldeleted−16
@@ -1,16 +0,0 @@
1−forwardChanges:
2− AtPath "/foo":
3− InOperation GetMethod:
4− WithStatusCode 200:
5− ResponsePayload:
6− PayloadSchema:
7− OfType Object:
8− InProperty "bar": UnguardedRecursion
9−backwardChanges:
10− AtPath "/foo":
11− InOperation GetMethod:
12− WithStatusCode 200:
13− ResponsePayload:
14− PayloadSchema:
15− OfType Object:
16− InProperty "bar": UnguardedRecursion
test/golden/common/unguarded-recursive/basic/trace-tree.yaml → test/golden/common/unguarded-recursive/trace-tree.yamlrenamed+2−2
@@ -1,10 +1,10 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/api/foo":
33 InOperation GetMethod:
44 WithStatusCode 200:
55 ResponsePayload:
66 PayloadSchema: UnguardedRecursion
7−backwardChanges:
7+nonBreakingChanges:
88 AtPath "/api/foo":
99 InOperation GetMethod:
1010 WithStatusCode 200:
test/golden/common/variant-added/report.md+7−7
@@ -1,24 +1,24 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 1 | 1 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 1 | 1 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
1212
1313#### `$(Object)`
1414
1515The case where `$.tag` is `"C"` – has been added.
1616
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
1818
1919## **POST** /test
2020
21−### ➡️☁️ JSON Request
21+### 📱➡️ JSON Request
2222
2323#### `$(Object)`
2424
test/golden/common/variant-added/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 WithStatusCode 200:
@@ -6,7 +6,7 @@ forwardChanges:
66 PayloadSchema:
77 OfType Object: PartitionBecomesEmpty (PInProperty "tag" PHere,CByEnumValue
88 (fromList [String "C"]))
9−backwardChanges:
9+nonBreakingChanges:
1010 AtPath "/test":
1111 InOperation PostMethod:
1212 InRequest:
test/golden/common/variant-changed/report.md+9−9
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 2 | 2 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 2 | 2 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### In cases where `$.tag` is `"B"`
1414
@@ -16,7 +16,7 @@
1616
1717The type has been removed.
1818
19−### ⬅️☁️ JSON Response – 200
19+### 📱⬅️ JSON Response – 200
2020
2121#### In cases where `$.tag` is `"B"`
2222
@@ -24,11 +24,11 @@ The type has been removed.
2424
2525The type has been added.
2626
27−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
27+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2828
2929## **POST** /test
3030
31−### ➡️☁️ JSON Request
31+### 📱➡️ JSON Request
3232
3333#### In cases where `$.tag` is `"B"`
3434
@@ -36,7 +36,7 @@ The type has been added.
3636
3737The type has been added.
3838
39−### ⬅️☁️ JSON Response – 200
39+### 📱⬅️ JSON Response – 200
4040
4141#### In cases where `$.tag` is `"B"`
4242
test/golden/common/variant-changed/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -15,7 +15,7 @@ forwardChanges:
1515 InPartition (PInProperty "tag" PHere,CByEnumValue (fromList [String "B"])):
1616 InProperty "prop_B":
1717 OfType String: TypeBecomesEmpty
18−backwardChanges:
18+nonBreakingChanges:
1919 AtPath "/test":
2020 InOperation PostMethod:
2121 InRequest:
test/golden/common/variant-deep-changed/report.md+9−9
@@ -1,14 +1,14 @@
11# Summary
22
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
4−|------------------------------------------|--------------------------------------------------|
5−| 2 | 2 |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5+| 2 | 2 | 0 |
66
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
88
99## **POST** /test
1010
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
1212
1313#### In cases where `$.desc.name` is `"B"`
1414
@@ -16,7 +16,7 @@
1616
1717The type has been removed.
1818
19−### ⬅️☁️ JSON Response – 200
19+### 📱⬅️ JSON Response – 200
2020
2121#### In cases where `$.desc.name` is `"B"`
2222
@@ -24,11 +24,11 @@ The type has been removed.
2424
2525The type has been added.
2626
27−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
27+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
2828
2929## **POST** /test
3030
31−### ➡️☁️ JSON Request
31+### 📱➡️ JSON Request
3232
3333#### In cases where `$.desc.name` is `"B"`
3434
@@ -36,7 +36,7 @@ The type has been added.
3636
3737The type has been added.
3838
39−### ⬅️☁️ JSON Response – 200
39+### 📱⬅️ JSON Response – 200
4040
4141#### In cases where `$.desc.name` is `"B"`
4242
test/golden/common/variant-deep-changed/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
22 AtPath "/test":
33 InOperation PostMethod:
44 InRequest:
@@ -15,7 +15,7 @@ forwardChanges:
1515 InPartition (PInProperty "desc" (PInProperty "name" PHere),CByEnumValue (fromList [String "B"])):
1616 InProperty "prop_B":
1717 OfType String: TypeBecomesEmpty
18−backwardChanges:
18+nonBreakingChanges:
1919 AtPath "/test":
2020 InOperation PostMethod:
2121 InRequest: