git.iko.soy/typeable/compaREST
on d50b2ba help sign in
.github/actions/common_setup/action.yamldeleted−42
since splitsnapshots
unifiedsplit
@@ -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 @@
1# Changelog
1# Changelog
2
2
3−`compaREST` uses [PVP Versioning][1].
3+`openapi-diff` uses [PVP Versioning][1].
4
4
5−## 0.1.0.1
5+## 0.0.0.0
6
6
7−* Fixed an issue related to empty commit SHAs in GitHub Actions.
7+* Initially created.
8−
9−## 0.1.0.0
10−
11−* Initial release.
12
8
13[1]: https://pvp.haskell.org
9[1]: https://pvp.haskell.org
LICENSE+1−1
@@ -1,6 +1,6 @@
1MIT License
1MIT License
2
2
3−Copyright (c) 2021 Typeable
3+Copyright (c) 2021 Aleksey Uymanov
4
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
6of this software and associated documentation files (the "Software"), to deal
README.md+13−273
@@ -1,293 +1,39 @@
1−# compaREST
1+# openapi-diff
2
2
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)
4[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
7
6Compatibility checker for OpenAPI
8Compatibility checker for OpenAPI
7
9
8−## Using compaREST in Github Actions
9
10
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```
11```
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)
12+Usage: openapi-diff (-c|--client ARG) (-s|--server ARG)
266− [--silent | --only-breaking | --all] [-o|--output ARG]
13+ [--silent | --only-breaking | --all] [-o|--output ARG]
267− [--folding-block-quotes-style | --header-style]
14+ [--folding-block-quotes-style | --header-style]
268− [--signal-exit-code]
269− A tool to check compatibility between two OpenAPI specifications.
15+ A tool to check compatibility between two OpenApi specifications.
270
16
271 Usage examples
17 Usage examples
272
18
273 Compare files old.yaml with new.yaml and output the resulting report to
19 Compare files old.yaml with new.yaml and output the resulting report to
274 stdout:
20 stdout:
275
21
276− comparest -c old.yaml -s new.yaml
22+ openapi-diff -c old.yaml -s new.yaml
277
23
278 Only output breaking changes and write a styled HTML report to file
24 Only output breaking changes and write a styled HTML report to file
279 report.html:
25 report.html:
280
26
281− comparest -c old.yaml -s new.yaml --only-breaking -o report
27+ openapi-diff -c old.yaml -s new.yaml --only-breaking -o report
282
28
283 Don't output anything, only fail if there are breaking changes:
29 Don't output anything, only fail if there are breaking changes:
284
30
285− comparest -c old.json -s new.json --silent
31+ openapi-diff -c old.json -s new.json --silent
286
32
287 Write full report suitable for embedding into a GitHub comment to
33 Write full report suitable for embedding into a GitHub comment to
288 report.html:
34 report.html:
289
35
290− comparest -c old.json -s new.json --folding-block-quotes-style -o report.html
36+ openapi-diff -c old.json -s new.json --folding-block-quotes-style -o report.html
291
37
292Available options:
38Available options:
293 -h,--help Show this help text
39 -h,--help Show this help text
@@ -297,8 +43,7 @@ Available options:
297 -s,--server ARG A path to the file containing the specification that
43 -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
44 will be used for the server of the API. Can be either
299 a YAML or JSON file.
45 a YAML or JSON file.
300− --silent Silence all output. Only makes sense in combination
46+ --silent Silence all output.
301− with --signal-exit-code.
302 --only-breaking Only report breaking changes in the output.
47 --only-breaking Only report breaking changes in the output.
303 --all Report both incompatible and compatible changes.
48 --all Report both incompatible and compatible changes.
304 Compatible changes will not trigger a failure exit
49 Compatible changes will not trigger a failure exit
@@ -322,9 +67,4 @@ Available options:
322 format. Markdown has rendering bugs on GitHub.
67 format. Markdown has rendering bugs on GitHub.
323 --header-style The report tree is structured using increasing levels
68 --header-style The report tree is structured using increasing levels
324 of headers.
69 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```
70```
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 @@
2-- Originally based on:
2-- Originally based on:
3-- https://github.com/jgm/pandoc/blob/master/src/Text/Pandoc/App/FormatHeuristics.hs
3-- https://github.com/jgm/pandoc/blob/master/src/Text/Pandoc/App/FormatHeuristics.hs
4module FormatHeuristic
4module FormatHeuristic
5− ( formatFromFilePath,
5+ ( formatFromFilePath
6 )
6 )
7where
7where
8
8
@@ -10,10 +10,10 @@ import Data.ByteString.Lazy (ByteString)
10import Data.Char (toLower)
10import Data.Char (toLower)
11import Data.Functor
11import Data.Functor
12import qualified Data.Map as M
12import qualified Data.Map as M
13−import qualified Data.OpenApi.Compare.Report.Html.Template as Html
14import Data.Text (Text)
13import Data.Text (Text)
15import qualified Data.Text.Lazy as TL
14import qualified Data.Text.Lazy as TL
16import qualified Data.Text.Lazy.Encoding as TL
15import qualified Data.Text.Lazy.Encoding as TL
16+import qualified OpenAPI.Checker.Report.Html.Template as Html
17import System.FilePath (takeExtension)
17import System.FilePath (takeExtension)
18import Text.DocTemplates.Internal
18import Text.DocTemplates.Internal
19import Text.Pandoc
19import Text.Pandoc
@@ -80,7 +80,7 @@ formatFromFilePath x =
80 def
80 def
81 { writerTemplate = Just Html.template
81 { writerTemplate = Just Html.template
82 , -- Not actually used. Needed to silence warning.
82 , -- 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))]
84 }
84 }
85 _ -> Nothing
85 _ -> Nothing
86 where
86 where
app/Main.hs+8−11
@@ -2,24 +2,22 @@ module Main (main) where
2
2
3import Control.Monad
3import Control.Monad
4import Control.Monad.Except
4import Control.Monad.Except
5−import Control.Monad.Trans (lift)
6import Data.Aeson
5import Data.Aeson
7import qualified Data.ByteString.Lazy as BSL
6import qualified Data.ByteString.Lazy as BSL
8import Data.Default
7import Data.Default
9import Data.Maybe
8import Data.Maybe
10−import Data.OpenApi.Compare.Options
11−import Data.OpenApi.Compare.Run
12import qualified Data.Text.IO as T
9import qualified Data.Text.IO as T
13import qualified Data.Yaml as Yaml
10import qualified Data.Yaml as Yaml
14import FormatHeuristic
11import FormatHeuristic
12+import OpenAPI.Checker.Options
13+import OpenAPI.Checker.Run
15import System.Exit
14import System.Exit
16import System.IO
15import System.IO
17import Text.Pandoc hiding (report)
16import Text.Pandoc hiding (report)
18−import Text.Pandoc.Builder
19
17
20main :: IO ()
18main :: IO ()
21main = do
19main = do
22− opts <- parseOptions
20+ opts <- execParser optionsParserInfo
23 let parseSchema path =
21 let parseSchema path =
24 eitherDecodeFileStrict path >>= \case
22 eitherDecodeFileStrict path >>= \case
25 Left jsonErr -> do
23 Left jsonErr -> do
@@ -49,13 +47,12 @@ main = do
49 }
47 }
50 (report, status) = runReport reportConfig (a, b)
48 (report, status) = runReport reportConfig (a, b)
51 case mode opts of
49 case mode opts of
52− Just _ -> either handler pure <=< runExceptT $ write $ doc report
50+ Just _ -> either handler pure <=< runExceptT $ write report
53 Nothing -> pure ()
51 Nothing -> pure ()
54− when (signalExitCode opts) $
55− case status of
52+ case status of
56− NoBreakingChanges -> exitSuccess
53+ NoBreakingChanges -> exitSuccess
57− BreakingChanges -> exitWith $ ExitFailure 1
54+ BreakingChanges -> exitWith $ ExitFailure 1
58− OnlyUnsupportedChanges -> exitWith $ ExitFailure 2
55+ OnlyUnsupportedChanges -> exitWith $ ExitFailure 2
59
56
60data Errors
57data Errors
61 = DocumentError PandocError
58 = DocumentError PandocError
app/OpenAPI/Checker/Options.hsnew file+105
@@ -0,0 +1,105 @@
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+import Options.Applicative.Help hiding (fullDesc)
13+
14+data Options = Options
15+ { clientFile :: FilePath
16+ , serverFile :: FilePath
17+ , -- | 'Nothing' means "don't produce any output"
18+ mode :: Maybe ReportMode
19+ , outputMode :: OutputMode
20+ , reportTreeStyle :: ReportTreeStyle
21+ }
22+ deriving stock (Generic)
23+
24+data OutputMode = StdoutMode | FileMode FilePath
25+
26+optionsParserInfo :: ParserInfo Options
27+optionsParserInfo =
28+ info
29+ (helper <*> optionsParser)
30+ (fullDesc
31+ <> header "openapi-diff"
32+ <> progDescDoc
33+ (Just $
34+ par "A tool to check compatibility between two OpenApi specifications."
35+ <$$> hardline <> par "Usage examples" <> hardline
36+ <$$> indent
37+ 4
38+ (par "Compare files old.yaml with new.yaml and output the resulting report to stdout:"
39+ <$$> hardline <> indent 4 "openapi-diff -c old.yaml -s new.yaml"
40+ <$$> hardline <> par "Only output breaking changes and write a styled HTML report to file report.html:"
41+ <$$> hardline <> indent 4 "openapi-diff -c old.yaml -s new.yaml --only-breaking -o report"
42+ <$$> hardline <> par "Don't output anything, only fail if there are breaking changes:"
43+ <$$> hardline <> indent 4 "openapi-diff -c old.json -s new.json --silent"
44+ <$$> hardline <> par "Write full report suitable for embedding into a GitHub comment to report.html:"
45+ <$$> hardline <> indent 4 "openapi-diff -c old.json -s new.json --folding-block-quotes-style -o report.html")))
46+
47+optionsParser :: Parser Options
48+optionsParser =
49+ Options
50+ <$> strOption
51+ (short 'c'
52+ <> long "client"
53+ <> help
54+ "A path to the file containing the specification that will be \
55+ \used for the client of the API. Can be either a YAML or JSON file.")
56+ <*> strOption
57+ (short 's'
58+ <> long "server"
59+ <> help
60+ "A path to the file containing the specification that will be \
61+ \used for the server of the API. Can be either a YAML or JSON file.")
62+ <*> (flag'
63+ Nothing
64+ (long "silent"
65+ <> help "Silence all output.")
66+ <|> flag'
67+ (Just OnlyErrors)
68+ (long "only-breaking"
69+ <> help "Only report breaking changes in the output.")
70+ <|> flag'
71+ (Just All)
72+ (long "all"
73+ <> help
74+ "Report both incompatible and compatible changes. \
75+ \Compatible changes will not trigger a failure exit code.")
76+ <|> pure (Just All))
77+ <*> ((FileMode
78+ <$> strOption
79+ (short 'o' <> long "output"
80+ <> helpDoc
81+ (Just $
82+ par "The file path where the output should be written. If the option is omitted the result will be written to stdout."
83+ <$$> hardline <> par "The file extension is used to determine the type of the output file."
84+ <$$> hardline <> par "Supports many formats such as markdown, html, rtf, doc, txt, rst, and many more."
85+ <$$> hardline <> par "Leave out the extension to produce a self-contained HTML report with styling.")))
86+ <|> pure StdoutMode)
87+ <*> (flag'
88+ FoldingBlockquotesTreeStyle
89+ (long "folding-block-quotes-style"
90+ <> help
91+ "The report tree is structured using \
92+ \summary/detail HTML elements and indented using \
93+ \block quotes. This style renders well on GitHub.\
94+ \Intended for HTML output format. Markdown has rendering \
95+ \bugs on GitHub.")
96+ <|> flag'
97+ HeadersTreeStyle
98+ (long "header-style"
99+ <> help
100+ "The report tree is structured using \
101+ \increasing levels of headers.")
102+ <|> pure HeadersTreeStyle)
103+
104+par :: String -> Doc
105+par = foldr1 (</>) . fmap string . words
awsm-cssnew file+1

Submodule changed.

benchmarks/README.md+2−2
@@ -1,6 +1,6 @@
1# OpenApi comparison benchmarks
1# OpenApi comparison benchmarks
2
2
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.
4
4
5To run the benchmarks run the following command in this directory:
5To run the benchmarks run the following command in this directory:
6
6
@@ -12,7 +12,7 @@ nix-build
12
12
13| Tool | Output file name |
13| Tool | Output file name |
14| -------------------------------------------------------------------------- | ------------------------------------------- |
14| -------------------------------------------------------------------------- | ------------------------------------------- |
15−| [Typeable CompaREST](https://github.com/typeable/compaREST) | `typeable.md` |
15+| [Typeable OpenApi Diff](https://github.com/typeable/openapi-diff) | `typeable.md` |
16| [Atlassian OpenApi Diff](https://bitbucket.org/atlassian/openapi-diff) | `atlassian.json`, `atlassian.error.txt` |
16| [Atlassian OpenApi Diff](https://bitbucket.org/atlassian/openapi-diff) | `atlassian.json`, `atlassian.error.txt` |
17| [OpenApi Tools OpenApi Diff](https://github.com/OpenAPITools/openapi-diff) | `openapitools.md`, `openapitools.error.txt` |
17| [OpenApi Tools OpenApi Diff](https://github.com/OpenAPITools/openapi-diff) | `openapitools.md`, `openapitools.error.txt` |
18| [Tufin OpenApi Diff](https://github.com/tufin/oasdiff) | `oasdiff.txt`, `oasdiff.error.txt` |
18| [Tufin OpenApi Diff](https://github.com/tufin/oasdiff) | `oasdiff.txt`, `oasdiff.error.txt` |
benchmarks/default.nix+12−8
@@ -1,6 +1,6 @@
1{ sources ? import ./nix/sources.nix
1{ sources ? import ./nix/sources.nix
2+, haskellNix ? import sources.haskellNix { }
2, gomod2nix ? sources.gomod2nix
3, gomod2nix ? sources.gomod2nix
3−, haskellNix ? (import ../. {}).haskellNix
4, pkgs ? import haskellNix.sources.nixpkgs-2105 (haskellNix.nixpkgsArgs // {
4, pkgs ? import haskellNix.sources.nixpkgs-2105 (haskellNix.nixpkgsArgs // {
5 overlays = haskellNix.nixpkgsArgs.overlays ++ [
5 overlays = haskellNix.nixpkgsArgs.overlays ++ [
6 (self: super: {
6 (self: super: {
@@ -8,19 +8,23 @@
8 })
8 })
9 ];
9 ];
10 })
10 })
11−, compaRESThsPkgs ? (import ../. {}).hsPkgs
12, npmNix ? import (sources.npmNix + "/npmPackages") { inherit pkgs; }
11, npmNix ? import (sources.npmNix + "/npmPackages") { inherit pkgs; }
13, mavenix ? import (sources.mavenix) { inherit pkgs; }
12, mavenix ? import (sources.mavenix) { inherit pkgs; }
14, bumpToken ? null
13, bumpToken ? null
15, bumpDocumentation ? null
14, bumpDocumentation ? null
16}:
15}:
17let
16let
18− typeable-comparest = compaRESThsPkgs.comparest.components.exes.comparest;
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;
19− typeable-comparest-exe = typeable-comparest + "/bin/comparest";
23+ typeable-openapi-diff-exe = typeable-openapi-diff + "/bin/openapi-diff";
20− typeable-comparest-differ = pkgs.writeScript "typeable-comparest-differ" ''
24+ typeable-openapi-diff-differ = pkgs.writeScript "typeable-openapi-diff-differ" ''
21 #!${pkgs.stdenv.shell}
25 #!${pkgs.stdenv.shell}
22 set +e
26 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
24 exit 0
28 exit 0
25 '';
29 '';
26
30
@@ -126,7 +130,7 @@ let
126 })
130 })
127 (getInputs root);
131 (getInputs root);
128 differs = [
132 differs = [
129− typeable-comparest-differ
133+ typeable-openapi-diff-differ
130 atlassian-openapi-diff-differ
134 atlassian-openapi-diff-differ
131 openapitools-openapi-diff-differ
135 openapitools-openapi-diff-differ
132 oasdiff-differ
136 oasdiff-differ
@@ -160,7 +164,7 @@ let
160 tools = [
164 tools = [
161 "Typeable"
165 "Typeable"
162 "Atlassian"
166 "Atlassian"
163− "OpenAPI Tools"
167+ "OpenApi Tools"
164 "oasdiff"
168 "oasdiff"
165 "Bump.sh"
169 "Bump.sh"
166 ];
170 ];
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.

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 @@
1cabal-version: 2.4
1cabal-version: 2.4
2−name: compaREST
2+name: openapi-diff
3−version: 0.1.0.1
3+version: 0.0.0.0
4synopsis: Compatibility checker for OpenAPI
4synopsis: Compatibility checker for OpenAPI
5−description: Compatibility checker for OpenAPI.
5+description: Compatibility checker for OpenAPI
6license: MIT
6license: MIT
7license-file: LICENSE
7license-file: LICENSE
8−author: Typeable
8+author: Aleksey Uymanov
9−maintainer: compaREST@typeable.io
9+maintainer: Aleksey Uymanov <s9gf4ult@gmail.com>
10−copyright: 2021 Typeable
10+copyright: 2021 Aleksey Uymanov
11category: Web
11category: Web
12build-type: Simple
12build-type: Simple
13extra-doc-files: README.md
13extra-doc-files: README.md
14 CHANGELOG.md
14 CHANGELOG.md
15−tested-with: GHC == 9.6
15+tested-with: GHC == 8.10.4
16−extra-source-files: css/awsm.min.css
16+extra-source-files: awsm-css/dist/awsm.min.css
17−extra-source-files:
17+
18− test/golden/**/*.yaml
18+Flag static
19− test/golden/**/*.md
19+ Default: False
20+ Manual: True
20
21
21common common-options
22common common-options
23+ if flag(static)
24+ cc-options: -static
25+ ld-options: -static -pthread
26+ ghc-options: -static
22 ghc-options:
27 ghc-options:
23 -Weverything
28 -Weverything
24 -Wno-implicit-prelude
29 -Wno-implicit-prelude
@@ -26,17 +31,16 @@ common common-options
26 -Wno-safe
31 -Wno-safe
27 -Wno-prepositive-qualified-module
32 -Wno-prepositive-qualified-module
28 -Wno-missing-import-lists
33 -Wno-missing-import-lists
29− -Wno-missing-kind-signatures
30 -Wno-partial-fields
34 -Wno-partial-fields
31 -Wno-all-missed-specialisations
35 -Wno-all-missed-specialisations
32 -Wno-missing-local-signatures
36 -Wno-missing-local-signatures
33 -Wno-unsafe
37 -Wno-unsafe
34 -fconstraint-solver-iterations=0
38 -fconstraint-solver-iterations=0
35 default-language: Haskell2010
39 default-language: Haskell2010
36− build-depends: base >= 4.12.0.0 && < 5
40+ build-depends: base >= 4.12.0.0 && < 4.16
41+ , bytestring
37 , text
42 , text
38
43
39−
40 default-extensions: ApplicativeDo
44 default-extensions: ApplicativeDo
41 , BangPatterns
45 , BangPatterns
42 , ConstraintKinds
46 , ConstraintKinds
@@ -78,7 +82,6 @@ common common-options
78 , QuantifiedConstraints
82 , QuantifiedConstraints
79 , DefaultSignatures
83 , DefaultSignatures
80
84
81−
82library
85library
83 import: common-options
86 import: common-options
84 build-depends:
87 build-depends:
@@ -108,53 +111,52 @@ library
108 , file-embed
111 , file-embed
109 , data-default
112 , data-default
110 , ordered-containers
113 , ordered-containers
111− , bytestring
112 hs-source-dirs: src
114 hs-source-dirs: src
113− exposed-modules: Data.OpenApi.Compare.Behavior
115+ exposed-modules: Data.HList
114− , Data.OpenApi.Compare.Common
116+ , OpenAPI.Checker.Behavior
115− , Data.OpenApi.Compare.Formula
117+ , OpenAPI.Checker.Formula
116− , Data.OpenApi.Compare.Memo
118+ , OpenAPI.Checker.Memo
117− , Data.OpenApi.Compare.Orphans
119+ , OpenAPI.Checker.Orphans
118− , Data.OpenApi.Compare.Paths
120+ , OpenAPI.Checker.Paths
119− , Data.OpenApi.Compare.PathsPrefixTree
121+ , OpenAPI.Checker.PathsPrefixTree
120− , Data.OpenApi.Compare.References
122+ , OpenAPI.Checker.References
121− , Data.OpenApi.Compare.Report
123+ , OpenAPI.Checker.Subtree
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
124+ , OpenAPI.Checker.Validate.MediaTypeObject
129− , Data.OpenApi.Compare.Validate.OAuth2Flows
130− , Data.OpenApi.Compare.Validate.OpenApi
125+ , OpenAPI.Checker.Validate.OpenApi
131− , Data.OpenApi.Compare.Validate.Operation
126+ , OpenAPI.Checker.Validate.Operation
132− , Data.OpenApi.Compare.Validate.Param
127+ , OpenAPI.Checker.Validate.Param
133− , Data.OpenApi.Compare.Validate.PathFragment
128+ , OpenAPI.Checker.Validate.PathFragment
134− , Data.OpenApi.Compare.Validate.Products
129+ , OpenAPI.Checker.Validate.Products
135− , Data.OpenApi.Compare.Validate.RequestBody
130+ , OpenAPI.Checker.Validate.RequestBody
136− , Data.OpenApi.Compare.Validate.Responses
131+ , OpenAPI.Checker.Validate.Responses
137− , Data.OpenApi.Compare.Validate.Schema
132+ , OpenAPI.Checker.Validate.Schema
133+ , OpenAPI.Checker.Validate.Schema.TypedJson
138− , Data.OpenApi.Compare.Validate.Schema.DNF
134+ , OpenAPI.Checker.Validate.Schema.DNF
139− , Data.OpenApi.Compare.Validate.Schema.Issues
135+ , OpenAPI.Checker.Validate.Schema.Issues
140− , Data.OpenApi.Compare.Validate.Schema.JsonFormula
136+ , OpenAPI.Checker.Validate.Schema.JsonFormula
141− , Data.OpenApi.Compare.Validate.Schema.Partition
137+ , OpenAPI.Checker.Validate.Schema.Partition
142− , Data.OpenApi.Compare.Validate.Schema.Process
138+ , OpenAPI.Checker.Validate.Schema.Process
143− , Data.OpenApi.Compare.Validate.Schema.Traced
139+ , OpenAPI.Checker.Validate.Schema.Traced
144− , Data.OpenApi.Compare.Validate.Schema.TypedJson
145− , Data.OpenApi.Compare.Validate.SecurityRequirement
140+ , OpenAPI.Checker.Validate.SecurityRequirement
146− , Data.OpenApi.Compare.Validate.SecurityScheme
147− , Data.OpenApi.Compare.Validate.Server
141+ , OpenAPI.Checker.Validate.Server
148− , Data.OpenApi.Compare.Validate.Sums
142+ , OpenAPI.Checker.Validate.Sums
149− other-modules: Data.HList
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
150 , Data.OpenUnion.Extra
149 , Data.OpenUnion.Extra
150+ , OpenAPI.Checker.Report.Jet
151+ , OpenAPI.Checker.Run
152+ , OpenAPI.Checker.Report.Html.Template
151
153
152−executable compaREST
154+executable openapi-diff
153 import: common-options
155 import: common-options
154 hs-source-dirs: app
156 hs-source-dirs: app
155 main-is: Main.hs
157 main-is: Main.hs
156 build-depends:
158 build-depends:
157− compaREST
159+ openapi-diff
158 , pandoc
160 , pandoc
159 , data-default
161 , data-default
160 , bytestring
162 , bytestring
@@ -165,42 +167,14 @@ executable compaREST
165 , aeson
167 , aeson
166 , containers
168 , containers
167 , doctemplates
169 , doctemplates
168− , pandoc-types
169− , prettyprinter-compat-ansi-wl-pprint
170 ghc-options: -threaded
170 ghc-options: -threaded
171 -rtsopts
171 -rtsopts
172 -with-rtsopts=-N
172 -with-rtsopts=-N
173 other-modules:
173 other-modules:
174 FormatHeuristic
174 FormatHeuristic
175− Data.OpenApi.Compare.Options
175+ OpenAPI.Checker.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
202
176
203−test-suite compaREST-tests
177+test-suite openapi-diff-test
204 import: common-options
178 import: common-options
205 type: exitcode-stdio-1.0
179 type: exitcode-stdio-1.0
206 hs-source-dirs: test
180 hs-source-dirs: test
@@ -208,10 +182,8 @@ test-suite compaREST-tests
208 other-modules:
182 other-modules:
209 Spec.Golden.Extra
183 Spec.Golden.Extra
210 Spec.Golden.TraceTree
184 Spec.Golden.TraceTree
211− Paths_compaREST
212− autogen-modules: Paths_compaREST
213 build-depends:
185 build-depends:
214− , compaREST
186+ , openapi-diff
215 , tasty-golden
187 , tasty-golden
216 , tasty
188 , tasty
217 , bytestring
189 , bytestring
@@ -221,7 +193,6 @@ test-suite compaREST-tests
221 , pandoc
193 , pandoc
222 , data-default
194 , data-default
223 , lens
195 , lens
224− , pandoc-types
225 ghc-options: -threaded
196 ghc-options: -threaded
226 -rtsopts
197 -rtsopts
227 -with-rtsopts=-N
198 -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 @@
1module Data.HList
1module Data.HList
2− ( Has,
2+ ( Has
3− HasAll,
3+ , HasAll
4− getH,
4+ , getH
5− HList (..),
5+ , HList (..)
6− singletonH,
6+ , singletonH
7− ReassembleHList,
7+ , ReassembleHList
8− reassemble,
8+ , reassemble
9 )
9 )
10where
10where
11
11
@@ -40,8 +40,8 @@ instance (Has' x xs t, HeadEq x (y : xs) ~ 'False) => Has' x (y ': xs) 'False wh
40 {-# INLINE getH #-}
40 {-# INLINE getH #-}
41
41
42instance
42instance
43− TypeError ( 'ShowType x ':<>: 'Text " is not a part of the list.") =>
43+ TypeError ('ShowType x ':<>: 'Text " is not a part of the list.")
44− Has' x '[] 'False
44+ => Has' x '[] 'False
45 where
45 where
46 getH HNil = undefined
46 getH HNil = undefined
47 {-# INLINE getH #-}
47 {-# INLINE getH #-}
@@ -74,8 +74,8 @@ instance ReassembleHList' (x ': xs) '[] 'False where
74 {-# INLINE reassemble #-}
74 {-# INLINE reassemble #-}
75
75
76instance
76instance
77− (Has y xs, ReassembleHList' xs ys f, (xs == (y ': ys)) ~ 'False) =>
77+ (Has y xs, ReassembleHList' xs ys f, (xs == (y ': ys)) ~ 'False)
78− ReassembleHList' xs (y ': ys) 'False
78+ => ReassembleHList' xs (y ': ys) 'False
79 where
79 where
80 reassemble xs = getH @y xs `HCons` reassemble xs
80 reassemble xs = getH @y xs `HCons` reassemble xs
81 {-# INLINE reassemble #-}
81 {-# 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 @@
1module Data.OpenUnion.Extra
1module Data.OpenUnion.Extra
2− ( (@@>),
2+ ( (@@>)
3− TryLiftUnion (..),
3+ , TryLiftUnion (..)
4− pattern SingletonUnion,
4+ , pattern SingletonUnion
5 )
5 )
6where
6where
7
7
@@ -18,8 +18,8 @@ instance TryLiftUnion '[] where
18 tryLiftUnion _ = empty
18 tryLiftUnion _ = empty
19
19
20instance
20instance
21− (Typeable y, SubList ys (y : ys), TryLiftUnion ys) =>
21+ (Typeable y, SubList ys (y : ys), TryLiftUnion ys)
22− TryLiftUnion (y ': ys)
22+ => TryLiftUnion (y ': ys)
23 where
23 where
24 tryLiftUnion (x :: x) = case eqT @x @y of
24 tryLiftUnion (x :: x) = case eqT @x @y of
25 Nothing -> reUnion <$> tryLiftUnion @ys x
25 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
1+module OpenAPI.Checker.Behavior
2− ( BehaviorLevel (..),
2+ ( BehaviorLevel (..)
3− Behavable (..),
3+ , Behavable (..)
4− IssueKind (..),
5− Issuable (..),
4+ , Issuable (..)
6− Orientation (..),
5+ , Orientation (..)
7− toggleOrientation,
6+ , toggleOrientation
8− Behavior,
7+ , Behavior
9− AnIssue (..),
8+ , AnIssue (..)
10− withClass,
11− anIssueKind,
12− relatedAnIssues,
13 )
9 )
14where
10where
15
11
16import Data.Aeson
12import Data.Aeson
17import Data.Kind
13import Data.Kind
18−import Data.OpenApi.Compare.Paths
19import Data.Typeable
14import Data.Typeable
15+import OpenAPI.Checker.Paths
20import Text.Pandoc.Builder
16import Text.Pandoc.Builder
21
17
22-- | Kind
18-- | Kind
@@ -43,23 +39,10 @@ class
43 Behavable (a :: BehaviorLevel) (b :: BehaviorLevel)
39 Behavable (a :: BehaviorLevel) (b :: BehaviorLevel)
44 where
40 where
45 data Behave a b
41 data Behave a b
46− describeBehavior :: Behave a b -> Inlines
42+ describeBehaviour :: Behave a b -> Inlines
47
43
48type instance AdditionalQuiverConstraints Behave a b = Behavable a b
44type instance AdditionalQuiverConstraints Behave a b = Behavable a b
49
45
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−
63class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLevel) where
46class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLevel) where
64 data Issue l :: Type
47 data Issue l :: Type
65
48
@@ -75,22 +58,7 @@ class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLeve
75 -- If _producer_ doesn't have something, the element was "added".
58 -- If _producer_ doesn't have something, the element was "added".
76 describeIssue :: Orientation -> Issue l -> Blocks
59 describeIssue :: Orientation -> Issue l -> Blocks
77
60
78− issueKind :: Issue l -> IssueKind
61+ issueIsUnsupported :: Issue l -> Bool
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
94
62
95data Orientation = Forward | Backward
63data Orientation = Forward | Backward
96 deriving stock (Eq, Ord, Show)
64 deriving stock (Eq, Ord, Show)
@@ -116,9 +84,3 @@ deriving stock instance Ord (AnIssue l)
116
84
117instance ToJSON (AnIssue l) where
85instance ToJSON (AnIssue l) where
118 toJSON (AnIssue _ issue) = toJSON issue
86 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
1+module OpenAPI.Checker.Common
2− ( zipAll,
2+ ( zipAll
3 )
3 )
4where
4where
5
5
src/Data/OpenApi/Compare/Formula.hs → src/OpenAPI/Checker/Formula.hsrenamed+25−35
@@ -1,21 +1,20 @@
1−module Data.OpenApi.Compare.Formula
1+module OpenAPI.Checker.Formula
2− ( FormulaF,
2+ ( FormulaF
3− VarRef,
3+ , VarRef
4− variable,
4+ , variable
5− eitherOf,
5+ , eitherOf
6− anError,
6+ , anError
7− errors,
7+ , errors
8− calculate,
8+ , calculate
9− maxFixpoint,
9+ , maxFixpoint
10− mapErrors,
10+ , mapErrors
11− )
12−where
11+ ) where
13
12
14import Data.Kind
13import Data.Kind
15−import qualified Data.List.NonEmpty as NE
16import Data.Monoid
14import Data.Monoid
17−import Data.OpenApi.Compare.Paths
15+import qualified Data.List.NonEmpty as NE
16+import OpenAPI.Checker.Paths
18−import qualified Data.OpenApi.Compare.PathsPrefixTree as P
17+import qualified OpenAPI.Checker.PathsPrefixTree as P
19
18
20type VarRef = Int
19type VarRef = Int
21
20
@@ -25,24 +24,15 @@ type VarRef = Int
25-- fixpoints always exist, i.e. that @x = f x@ has at least one solution.
24-- fixpoints always exist, i.e. that @x = f x@ has at least one solution.
26data FormulaF (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: Type) where
25data FormulaF (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: Type) where
27 Result :: a -> FormulaF q f r a
26 Result :: a -> FormulaF q f r a
28− Errors ::
29− !(P.PathsPrefixTree q f r) ->
27+ Errors :: !(P.PathsPrefixTree q f r) -> FormulaF q f r a
30− -- | invariant: never empty
28+ -- ^ invariant: never empty
31− FormulaF q f r a
32− Apply ::
33− FormulaF q f r (b -> c) ->
29+ Apply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
34− FormulaF q f r b ->
35− (c -> a) ->
36− -- | invariant: at least one of LHS and RHS is not 'Errors', and they are
30+ -- ^ invariant: at least one of LHS and RHS is not 'Errors', and they are
37 -- both not 'Result'
31 -- both not 'Result'
38− FormulaF q f r a
39− SelectFirst ::
40− NE.NonEmpty (SomeFormulaF b) ->
32+ SelectFirst :: NE.NonEmpty (SomeFormulaF b)
41− !(P.PathsPrefixTree q f r) ->
33+ -> !(P.PathsPrefixTree q f r) -> (b -> a) -> FormulaF q f r a
42− (b -> a) ->
43− -- | invariant: the list doesn't contain any 'Result's, 'Errors' or
34+ -- ^ invariant: the list doesn't contain any 'Result's, 'Errors' or
44 -- 'SelectFirst'
35 -- 'SelectFirst'
45− FormulaF q f r a
46 Variable :: !VarRef -> a -> FormulaF q f r a
36 Variable :: !VarRef -> a -> FormulaF q f r a
47
37
48mkApply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
38mkApply :: 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
54mkSelectFirst :: [SomeFormulaF b] -> P.PathsPrefixTree q f r -> (b -> a) -> FormulaF q f r a
44mkSelectFirst :: [SomeFormulaF b] -> P.PathsPrefixTree q f r -> (b -> a) -> FormulaF q f r a
55mkSelectFirst fs allE h = case foldMap check fs of
45mkSelectFirst fs allE h = case foldMap check fs of
56 (First (Just x), _) -> Result (h x)
46 (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
58 (First Nothing, []) -> Errors allE
48 (First Nothing, []) -> Errors allE
59 where
49 where
60 check (SomeFormulaF (Result x)) = (First (Just x), mempty)
50 check (SomeFormulaF (Result x)) = (First (Just x), mempty)
61 check (SomeFormulaF (Errors _)) = (mempty, mempty)
51 check (SomeFormulaF (Errors _)) = (mempty, mempty)
62− check (SomeFormulaF (SelectFirst xs _ h')) =
52+ check (SomeFormulaF (SelectFirst xs _ h'))
63− (mempty, NE.toList (fmap (fmap h') xs))
53+ = (mempty, NE.toList (fmap (fmap h') xs))
64 check x = (mempty, [x])
54 check x = (mempty, [x])
65
55
66data SomeFormulaF (a :: Type) where
56data SomeFormulaF (a :: Type) where
@@ -106,7 +96,7 @@ calculate (Apply f x h) = case calculate f of
106 Right x' -> Right (h (f' x'))
96 Right x' -> Right (h (f' x'))
107calculate (SelectFirst xs e h) = go (NE.toList xs)
97calculate (SelectFirst xs e h) = go (NE.toList xs)
108 where
98 where
109− go (SomeFormulaF r : rs) = case calculate r of
99+ go (SomeFormulaF r:rs) = case calculate r of
110 Left _ -> go rs
100 Left _ -> go rs
111 Right x -> Right (h x)
101 Right x -> Right (h x)
112 go [] = Left e
102 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 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Orphans () where
3+module OpenAPI.Checker.Orphans () where
4
4
5import Control.Comonad.Env
5import Control.Comonad.Env
6import qualified Data.HashMap.Strict.InsOrd as IOHM
6import qualified Data.HashMap.Strict.InsOrd as IOHM
src/Data/OpenApi/Compare/Paths.hs → src/OpenAPI/Checker/Paths.hsrenamed+10−10
@@ -1,18 +1,18 @@
1-- | Utilities for traversing heterogeneous trees. A heterogeneous tree is a
1-- | Utilities for traversing heterogeneous trees. A heterogeneous tree is a
2-- collection of datatypes that "contain" eachother in some form of tree
2-- collection of datatypes that "contain" eachother in some form of tree
3-- structure.
3-- structure.
4−module Data.OpenApi.Compare.Paths
4+module OpenAPI.Checker.Paths
5− ( NiceQuiver,
5+ ( NiceQuiver
6− AdditionalQuiverConstraints,
6+ , AdditionalQuiverConstraints
7− Paths (..),
7+ , Paths (..)
8− DiffPaths (..),
8+ , DiffPaths (..)
9− catDiffPaths,
9+ , catDiffPaths
10− AnItem (..),
10+ , AnItem (..)
11− step,
11+ , step
12
12
13 -- * Reexports
13 -- * Reexports
14− (>>>),
14+ , (>>>)
15− (<<<),
15+ , (<<<)
16 )
16 )
17where
17where
18
18
src/Data/OpenApi/Compare/PathsPrefixTree.hs → src/OpenAPI/Checker/PathsPrefixTree.hsrenamed+42−65
@@ -1,42 +1,39 @@
1{-# LANGUAGE QuantifiedConstraints #-}
1{-# LANGUAGE QuantifiedConstraints #-}
2
2
3−module Data.OpenApi.Compare.PathsPrefixTree
3+module OpenAPI.Checker.PathsPrefixTree
4− ( PathsPrefixTree (PathsPrefixNode),
4+ ( PathsPrefixTree (PathsPrefixNode)
5− AStep (..),
5+ , AStep (..)
6− empty,
6+ , empty
7− singleton,
7+ , singleton
8− fromList,
8+ , fromList
9− null,
9+ , null
10− foldWith,
10+ , foldWith
11− toList,
11+ , toList
12− filter,
12+ , filter
13− filterWithKey,
14− takeSubtree,
13+ , takeSubtree
15− lookup,
16− embed,
14+ , embed
17− size,
15+ , size
18− partition,
16+ , partition
19− map,
17+ , map
20 )
18 )
21where
19where
22
20
23import Control.Monad
21import Control.Monad
24import Data.Aeson
22import Data.Aeson
25import Data.Foldable hiding (null, toList)
23import Data.Foldable hiding (null, toList)
26−import qualified Data.Aeson.Key as Key
24+import qualified Data.HashMap.Strict as HM
27−import qualified Data.Aeson.KeyMap as KeyMap
28import Data.Kind
25import Data.Kind
29import qualified Data.Map as M
26import qualified Data.Map as M
30import Data.Monoid
27import Data.Monoid
31−import Data.OpenApi.Compare.Paths
32import qualified Data.Set as S
28import qualified Data.Set as S
33import qualified Data.Text as T
29import qualified Data.Text as T
34import Data.Type.Equality
30import Data.Type.Equality
35import qualified Data.TypeRepMap as TRM
31import qualified Data.TypeRepMap as TRM
36import qualified Data.Vector as V
32import qualified Data.Vector as V
37import qualified GHC.Exts as Exts
33import qualified GHC.Exts as Exts
34+import OpenAPI.Checker.Paths
38import Type.Reflection
35import Type.Reflection
39−import Prelude hiding (filter, lookup, map, null)
36+import Prelude hiding (filter, map, null)
40
37
41-- | A list of @AnItem r f@, but optimized into a prefix tree.
38-- | A list of @AnItem r f@, but optimized into a prefix tree.
42data PathsPrefixTree (q :: k -> k -> Type) (f :: k -> Type) (r :: k) = PathsPrefixTree
39data PathsPrefixTree (q :: k -> k -> Type) (f :: k -> Type) (r :: k) = PathsPrefixTree
@@ -59,27 +56,10 @@ filter f (PathsPrefixTree roots branches) = PathsPrefixTree roots' branches'
59 roots' = filterASet f roots
56 roots' = filterASet f roots
60 branches' =
57 branches' =
61 Exts.fromList
58 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)
63 . Exts.toList
60 . Exts.toList
64 $ branches
61 $ branches
65
62
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−
83-- | The number of leaves.
63-- | The number of leaves.
84size :: PathsPrefixTree q f r -> Int
64size :: PathsPrefixTree q f r -> Int
85size (PathsPrefixTree root branches) =
65size (PathsPrefixTree root branches) =
@@ -103,11 +83,11 @@ deriving stock instance Eq (PathsPrefixTree q f a)
103
83
104-- Kind of orphan. Treat the map as an infinite tuple of @Maybe (f a)@'s, where
84-- Kind of orphan. Treat the map as an infinite tuple of @Maybe (f a)@'s, where
105-- the components are ordered by the @SomeTypeRep@ of the @a@.
85-- the components are ordered by the @SomeTypeRep@ of the @a@.
106−compareTRM ::
86+compareTRM
107− (forall a. Typeable a => Ord (f a)) =>
87+ :: (forall a. Typeable a => Ord (f a))
108− TRM.TypeRepMap f ->
88+ => TRM.TypeRepMap f
109− TRM.TypeRepMap f ->
89+ -> TRM.TypeRepMap f
110− Ordering
90+ -> Ordering
111compareTRM s1 s2 =
91compareTRM s1 s2 =
112 foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
92 foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
113 where
93 where
@@ -117,11 +97,11 @@ compareTRM s1 s2 =
117 compareMaybe _ Nothing (Just _) = LT
97 compareMaybe _ Nothing (Just _) = LT
118 compareMaybe _ (Just _) Nothing = GT
98 compareMaybe _ (Just _) Nothing = GT
119 compareMaybe cmp (Just x) (Just y) = cmp x y
99 compareMaybe cmp (Just x) (Just y) = cmp x y
120− compareW ::
100+ compareW
121− (forall a. Typeable a => Ord (f a)) =>
101+ :: (forall a. Typeable a => Ord (f a))
122− TRM.WrapTypeable f ->
102+ => TRM.WrapTypeable f
123− TRM.WrapTypeable f ->
103+ -> TRM.WrapTypeable f
124− Ordering
104+ -> Ordering
125 compareW (TRM.WrapTypeable (x :: f a)) (TRM.WrapTypeable (y :: f b))
105 compareW (TRM.WrapTypeable (x :: f a)) (TRM.WrapTypeable (y :: f b))
126 | Just Refl <- testEquality (typeRep @a) (typeRep @b) = compare x y
106 | Just Refl <- testEquality (typeRep @a) (typeRep @b) = compare x y
127 | otherwise = EQ -- unreachable
107 | otherwise = EQ -- unreachable
@@ -173,10 +153,10 @@ instance Monoid (ASet a) where
173 mempty = AnEmptySet
153 mempty = AnEmptySet
174
154
175data AStep (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: k) where
155data AStep (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: k) where
176− AStep ::
156+ AStep
177− NiceQuiver q r a =>
157+ :: NiceQuiver q r a =>
178− !(M.Map (q r a) (PathsPrefixTree q f a)) ->
158+ !(M.Map (q r a) (PathsPrefixTree q f a))
179− AStep q f r a
159+ -> AStep q f r a
180
160
181mapAStep :: (forall x. f x -> f x) -> AStep q f r a -> AStep q f r a
161mapAStep :: (forall x. f x -> f x) -> AStep q f r a -> AStep q f r a
182mapAStep f (AStep m) = AStep $ M.map (map f) m
162mapAStep f (AStep m) = AStep $ M.map (map f) m
@@ -216,12 +196,12 @@ null :: PathsPrefixTree q f r -> Bool
216null (PathsPrefixTree AnEmptySet s) = all (\(TRM.WrapTypeable (AStep x)) -> all null x) (Exts.toList s)
196null (PathsPrefixTree AnEmptySet s) = all (\(TRM.WrapTypeable (AStep x)) -> all null x) (Exts.toList s)
217null _ = False
197null _ = False
218
198
219−foldWith ::
199+foldWith
220− forall q f m r.
200+ :: forall q f m r.
221− Monoid m =>
201+ Monoid m
222− (forall a. Ord (f a) => Paths q r a -> f a -> m) ->
202+ => (forall a. Ord (f a) => Paths q r a -> f a -> m)
223− PathsPrefixTree q f r ->
203+ -> PathsPrefixTree q f r
224− m
204+ -> m
225foldWith k = goTPT Root
205foldWith k = goTPT Root
226 where
206 where
227 goTPT :: forall a. Paths q r a -> PathsPrefixTree q f a -> m
207 goTPT :: forall a. Paths q r a -> PathsPrefixTree q f a -> m
@@ -245,9 +225,6 @@ takeSubtree (Snoc xs x) t =
245 foldMap (\(AStep m) -> fold $ M.lookup x m) $
225 foldMap (\(AStep m) -> fold $ M.lookup x m) $
246 TRM.lookup @a $ snocItems $ takeSubtree xs t
226 TRM.lookup @a $ snocItems $ takeSubtree xs t
247
227
248−lookup :: Paths q r a -> PathsPrefixTree q f r -> S.Set (f a)
249−lookup xs = toSet . rootItems . takeSubtree xs
250−
251-- | Embed a subtree in a larger tree with given prefix
228-- | Embed a subtree in a larger tree with given prefix
252embed :: Paths q r a -> PathsPrefixTree q f a -> PathsPrefixTree q f r
229embed :: Paths q r a -> PathsPrefixTree q f a -> PathsPrefixTree q f r
253embed Root t = t
230embed Root t = t
@@ -257,7 +234,7 @@ newtype MergableObject = MergableObject {getMergableObject :: Object}
257
234
258instance Semigroup MergableObject where
235instance Semigroup MergableObject where
259 (MergableObject x) <> (MergableObject y) =
236 (MergableObject x) <> (MergableObject y) =
260− MergableObject $ KeyMap.unionWith mergeValue x y
237+ MergableObject $ HM.unionWith mergeValue x y
261 where
238 where
262 mergeValue :: Value -> Value -> Value
239 mergeValue :: Value -> Value -> Value
263 mergeValue (Object a) (Object b) =
240 mergeValue (Object a) (Object b) =
@@ -272,6 +249,6 @@ instance Monoid MergableObject where
272
249
273traceObject :: Paths q r a -> Value -> Object
250traceObject :: Paths q r a -> Value -> Object
274traceObject Root (Object o) = o
251traceObject Root (Object o) = o
275−traceObject Root v = KeyMap.singleton "root" v
252+traceObject Root v = HM.singleton "root" v
276traceObject (root `Snoc` s) v =
253traceObject (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 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.References
3+module OpenAPI.Checker.References
4− ( Step (..),
4+ ( Step (..)
5− dereference,
5+ , dereference
6− Typeable,
6+ , Typeable
7 )
7 )
8where
8where
9
9
@@ -11,18 +11,19 @@ import Data.HList
11import qualified Data.HashMap.Strict.InsOrd as IOHM
11import qualified Data.HashMap.Strict.InsOrd as IOHM
12import Data.Maybe
12import Data.Maybe
13import Data.OpenApi
13import Data.OpenApi
14−import Data.OpenApi.Compare.Orphans ()
14+import Data.Typeable
15+import OpenAPI.Checker.Orphans ()
15−import Data.OpenApi.Compare.Subtree
16+import OpenAPI.Checker.Subtree
16
17
17instance Typeable a => Steppable (Referenced a) a where
18instance Typeable a => Steppable (Referenced a) a where
18 data Step (Referenced a) a = InlineStep
19 data Step (Referenced a) a = InlineStep
19 deriving stock (Eq, Ord, Show)
20 deriving stock (Eq, Ord, Show)
20
21
21−dereference ::
22+dereference
22− Typeable a =>
23+ :: Typeable a
23− Traced (Definitions a) ->
24+ => Traced (Definitions a)
24− Traced (Referenced a) ->
25+ -> Traced (Referenced a)
25− Traced a
26+ -> Traced a
26dereference defs x = case extract x of
27dereference defs x = case extract x of
27 Inline a ->
28 Inline a ->
28 traced (ask x >>> step InlineStep) a
29 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
1+module OpenAPI.Checker.Report
2− ( generateReport,
2+ ( generateReport
3− CheckerOutput (..),
4− ReportInput (..),
3+ , ReportInput (..)
5− segregateIssues,
6− ReportStatus (..),
4+ , ReportStatus (..)
7− Pandoc,
5+ , Pandoc
8− ReportConfig (..),
6+ , ReportConfig (..)
9− ReportTreeStyle (..),
7+ , ReportTreeStyle (..)
10− ReportMode (..),
8+ , ReportMode (..)
11 )
9 )
12where
10where
13
11
@@ -23,15 +21,6 @@ import qualified Data.Map as M
23import Data.Map.Ordered (OMap)
21import Data.Map.Ordered (OMap)
24import qualified Data.Map.Ordered as OM
22import qualified Data.Map.Ordered as OM
25import Data.Maybe
23import 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
35import Data.OpenUnion
24import Data.OpenUnion
36import Data.OpenUnion.Extra
25import Data.OpenUnion.Extra
37import Data.Set
26import Data.Set
@@ -41,47 +30,27 @@ import qualified Data.Text as T
41import Data.TypeRepMap hiding (empty)
30import Data.TypeRepMap hiding (empty)
42import Data.Typeable
31import Data.Typeable
43import Generic.Data
32import 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
44import Text.Pandoc.Builder
42import Text.Pandoc.Builder
45
43
46type Changes = P.PathsPrefixTree Behave AnIssue 'APILevel
44type Changes = P.PathsPrefixTree Behave AnIssue 'APILevel
47
45
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−
56data ReportInput = ReportInput
46data ReportInput = ReportInput
57− { -- | forward 'CertainIssue', 'ProbablyIssue' and 'Comment'
58− breakingChanges :: Changes
47+ { breakingChanges :: Changes
59− , -- | backward 'CertainIssue', 'ProbablyIssue' and 'Comment', except those shadowed by 'relatedIssues'
60− nonBreakingChanges :: Changes
48+ , 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
65 }
49 }
66 deriving stock (Generic)
50 deriving stock (Generic)
67 deriving (Semigroup, Monoid) via (Generically ReportInput)
51 deriving (Semigroup, Monoid) via (Generically ReportInput)
68 deriving anyclass (ToJSON)
52 deriving anyclass (ToJSON)
69
53
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−
85data ReportStatus
54data ReportStatus
86 = BreakingChanges
55 = BreakingChanges
87 | NoBreakingChanges
56 | NoBreakingChanges
@@ -109,66 +78,49 @@ data ReportTreeStyle = HeadersTreeStyle | FoldingBlockquotesTreeStyle
109twoRowTable :: [(Inlines, Inlines)] -> Blocks
78twoRowTable :: [(Inlines, Inlines)] -> Blocks
110twoRowTable x = simpleTable (para . fst <$> x) [para . snd <$> x]
79twoRowTable x = simpleTable (para . fst <$> x) [para . snd <$> x]
111
80
112−generateReport :: ReportConfig -> ReportInput -> (Blocks, ReportStatus)
81+generateReport :: ReportConfig -> ReportInput -> (Pandoc, ReportStatus)
113generateReport cfg inp =
82generateReport cfg inp =
114− let schemaIssuesPresent = not $ P.null $ schemaIssues 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
115− breakingChangesPresent = not $ P.null $ breakingChanges inp
87+ breakingChangesPresent = not $ P.null breaking
116− nonBreakingChangesPresent = not $ P.null $ nonBreakingChanges inp
88+ nonBreakingChangesPresent = not $ P.null nonBreaking
117− unsupportedChangesPresent = not $ P.null $ unsupportedChanges inp
89+ unsupportedChangesPresent = not $ P.null unsupported
118 nonBreakingChangesShown = case reportMode cfg of
90 nonBreakingChangesShown = case reportMode cfg of
119 All -> True
91 All -> True
120 OnlyErrors -> False
92 OnlyErrors -> False
121 builder = buildReport cfg
93 builder = buildReport cfg
122 report =
94 report =
95+ doc $
123− header 1 "Summary"
96+ header 1 "Summary"
124− <> twoRowTable
97+ <> twoRowTable
125− ( when'
98+ ([ ( refOpt breakingChangesPresent breakingChangesId "⚠️ Breaking changes"
126− schemaIssuesPresent
127− [
128− ( refOpt schemaIssuesPresent schemaIssuesId "‼️ Schema issues"
129− , show' $ P.size $ schemaIssues inp
99+ , show' $ P.size breaking
130− )
100+ )
131− ]
101+ ]
132− ++ [
102+ ++ when'
133− ( refOpt breakingChangesPresent breakingChangesId "❌ Breaking changes"
103+ nonBreakingChangesShown
134− , show' $ P.size $ breakingChanges inp
104+ [ ( refOpt nonBreakingChangesPresent nonBreakingChangesId "🙆 Non-breaking changes"
105+ , show' $ P.size nonBreaking
135 )
106 )
136 ]
107 ]
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"
108+ ++ [ ( refOpt unsupportedChangesPresent unsupportedChangesId "🤷 Unsupported feature changes"
148− , show' $ P.size $ unsupportedChanges inp
109+ , show' $ P.size unsupported
149− )
110+ )
150− ]
111+ ])
151− )
152− <> when'
153− schemaIssuesPresent
154− ( header 1 (anchor schemaIssuesId <> "‼️ Schema issues")
155− <> builder (showErrs $ schemaIssues inp)
156− )
157− <> when'
112+ <> when'
158− breakingChangesPresent
113+ breakingChangesPresent
159− ( header 1 (anchor breakingChangesId <> "❌ Breaking changes")
114+ (header 1 (anchor breakingChangesId <> "⚠️ Breaking changes")
160− <> builder (showErrs $ breakingChanges inp)
115+ <> builder (showErrs breaking))
161− )
162− <> when'
116+ <> when'
163− (nonBreakingChangesPresent && nonBreakingChangesShown)
117+ (nonBreakingChangesPresent && nonBreakingChangesShown)
164− ( header 1 (anchor nonBreakingChangesId <> "⚠️ Non-breaking changes")
118+ (header 1 (anchor nonBreakingChangesId <> "🙆 Non-breaking changes")
165− <> builder (showErrs $ nonBreakingChanges inp)
119+ <> builder (showErrs nonBreaking))
166− )
167− <> when'
120+ <> when'
168− unsupportedChangesPresent
121+ unsupportedChangesPresent
169− ( header 1 (anchor unsupportedChangesId <> "❓ Unsupported feature changes")
122+ (header 1 (anchor unsupportedChangesId <> "🤷 Unsupported feature changes")
170− <> builder (showErrs $ unsupportedChanges inp)
123+ <> builder (showErrs unsupported))
171− )
172 status =
124 status =
173 if
125 if
174 | breakingChangesPresent -> BreakingChanges
126 | breakingChangesPresent -> BreakingChanges
@@ -183,11 +135,10 @@ generateReport cfg inp =
183 refOpt False _ i = i
135 refOpt False _ i = i
184 refOpt True a i = link ("#" <> a) "" i
136 refOpt True a i = link ("#" <> a) "" i
185
137
186− breakingChangesId, nonBreakingChangesId, unsupportedChangesId, schemaIssuesId :: Text
138+ breakingChangesId, nonBreakingChangesId, unsupportedChangesId :: Text
187 breakingChangesId = "breaking-changes"
139 breakingChangesId = "breaking-changes"
188 unsupportedChangesId = "unsupported-changes"
140 unsupportedChangesId = "unsupported-changes"
189 nonBreakingChangesId = "non-breaking-changes"
141 nonBreakingChangesId = "non-breaking-changes"
190− schemaIssuesId = "schema-issues"
191
142
192 when' :: Monoid m => Bool -> m -> m
143 when' :: Monoid m => Bool -> m -> m
193 when' True m = m
144 when' True m = m
@@ -202,10 +153,9 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
202 Just Refl
153 Just Refl
203 | (S.toList -> p@((AnIssue ori _) : _), o) <-
154 | (S.toList -> p@((AnIssue ori _) : _), o) <-
204 S.partition
155 S.partition
205− ( \((AnIssue _ u)) -> case u of
156+ (\((AnIssue _ u)) -> case u of
206− NoPathsMatched {} -> True
157+ NoPathsMatched {} -> True
207− AllPathsFailed {} -> True
158+ AllPathsFailed {} -> True)
208− )
209 currentIssues ->
159 currentIssues ->
210 let p' = p <&> (\(AnIssue _ i) -> i)
160 let p' = p <&> (\(AnIssue _ i) -> i)
211 in (Just (ori, p'), o)
161 in (Just (ori, p'), o)
@@ -216,10 +166,9 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
216 paths = case removedPaths of
166 paths = case removedPaths of
217 Just (ori, ps) -> do
167 Just (ori, ps) -> do
218 singletonHeader
168 singletonHeader
219− ( case ori of
169+ (case ori of
220− Forward -> "Removed paths"
170+ Forward -> "Removed paths"
221− Backward -> "Added paths"
171+ Backward -> "Added paths")
222− )
223 $ singletonBody $
172 $ singletonBody $
224 bulletList $
173 bulletList $
225 ps <&> \case
174 ps <&> \case
@@ -231,7 +180,7 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
231 flip foldMap (M.toList m) $ \(bhv, subErrors) ->
180 flip foldMap (M.toList m) $ \(bhv, subErrors) ->
232 if P.null subErrors
181 if P.null subErrors
233 then mempty
182 then mempty
234− else singletonHeader (describeBehavior bhv) $ showErrs subErrors
183+ else singletonHeader (describeBehaviour bhv) $ showErrs subErrors
235 in issues <> paths <> rest
184 in issues <> paths <> rest
236
185
237unfoldFunctions :: forall m a. (Monoid m, Eq a) => a -> [a -> (m, a)] -> (a -> m) -> m
186unfoldFunctions :: forall m a. (Monoid m, Eq a) => a -> [a -> (m, a)] -> (a -> m) -> m
@@ -249,68 +198,66 @@ jets =
249 unwrapReportJetResult
198 unwrapReportJetResult
250 <$> [ constructReportJet $
199 <$> [ constructReportJet $
251 curry $ \case
200 curry $ \case
252− (OfType Object, p@(InPartition _)) -> Just $ describeBehavior p :: Maybe Inlines
201+ (OfType Object, p@(InPartition _)) -> Just $ describeBehaviour p :: Maybe Inlines
253 _ -> Nothing
202 _ -> Nothing
254 , constructReportJet jsonPathJet
203 , constructReportJet jsonPathJet
255 , constructReportJet $ \p@(AtPath _) op@(InOperation _) ->
204 , constructReportJet $ \p@(AtPath _) op@(InOperation _) ->
256− strong (describeBehavior op) <> " " <> describeBehavior p :: Inlines
205+ strong (describeBehaviour op) <> " " <> describeBehaviour p :: Inlines
257 , constructReportJet $ \(WithStatusCode c) ResponsePayload PayloadSchema ->
206 , constructReportJet $ \(WithStatusCode c) ResponsePayload PayloadSchema ->
258− "⬅️☁️ JSON Response – " <> str (T.pack . show $ c) :: Inlines
207+ "📱⬅️ JSON Response – " <> str (T.pack . show $ c) :: Inlines
259− , constructReportJet $ \InRequest InPayload PayloadSchema -> "➡️☁️ JSON Request" :: Inlines
208+ , constructReportJet $ \InRequest InPayload PayloadSchema -> "📱➡️ JSON Request" :: Inlines
260 ]
209 ]
261 where
210 where
262 unwrapReportJetResult :: ReportJetResult Behave x -> ReportJet' Behave x
211 unwrapReportJetResult :: ReportJetResult Behave x -> ReportJet' Behave x
263 unwrapReportJetResult (Pure _) = error "There really shouldn't be any results here."
212 unwrapReportJetResult (Pure _) = error "There really shouldn't be any results here."
264 unwrapReportJetResult (Free f) = f
213 unwrapReportJetResult (Free f) = f
265
214
266− jsonPathJet ::
215+ jsonPathJet
267− NonEmpty
216+ :: NonEmpty
268− ( Union
217+ ( Union
269− '[ Behave 'SchemaLevel 'TypedSchemaLevel
218+ '[ Behave 'SchemaLevel 'TypedSchemaLevel
270− , Behave 'TypedSchemaLevel 'SchemaLevel
219+ , Behave 'TypedSchemaLevel 'SchemaLevel
271− ]
220+ ]
272− ) ->
221+ )
273− Inlines
222+ -> Inlines
274 jsonPathJet x = code $ "$" <> showParts (NE.toList x)
223 jsonPathJet x = code $ "$" <> showParts (NE.toList x)
275 where
224 where
276− showParts ::
225+ showParts
277− [ Union
226+ :: [ Union
278− '[ Behave 'SchemaLevel 'TypedSchemaLevel
227+ '[ Behave 'SchemaLevel 'TypedSchemaLevel
279− , Behave 'TypedSchemaLevel 'SchemaLevel
228+ , Behave 'TypedSchemaLevel 'SchemaLevel
280− ]
229+ ]
281− ] ->
230+ ]
282− Text
231+ -> Text
283 showParts [] = mempty
232 showParts [] = mempty
284 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion (InProperty _)) : _)) = showParts xs
233 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion (InProperty _)) : _)) = showParts xs
285 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion InAdditionalProperty) : _)) = showParts xs
234 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion InAdditionalProperty) : _)) = showParts xs
286 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion InItems : _)) = showParts xs
235 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion InItems : _)) = showParts xs
287 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion (InItem _) : _)) = showParts xs
236 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion (InItem _) : _)) = showParts xs
288 showParts (y : ys) =
237 showParts (y : ys) =
289− ( (\(OfType t) -> "(" <> describeJSONType t <> ")")
238+ ((\(OfType t) -> "(" <> describeJSONType t <> ")")
290− @@> ( \case
239+ @@> (\case
291− InItems -> "[*]"
240+ InItems -> "[*]"
292− InItem i -> "[" <> T.pack (show i) <> "]"
241+ InItem i -> "[" <> T.pack (show i) <> "]"
293− InProperty p -> "." <> p
242+ InProperty p -> "." <> p
294− InAdditionalProperty -> ".*"
243+ InAdditionalProperty -> ".*")
295− )
296− @@> typesExhausted
244+ @@> typesExhausted)
297− )
298 y
245 y
299 <> showParts ys
246 <> showParts ys
300
247
301−observeJetShowErrs ::
248+observeJetShowErrs
302− ReportJet' Behave (Maybe Inlines) ->
249+ :: ReportJet' Behave (Maybe Inlines)
303− P.PathsPrefixTree Behave AnIssue a ->
250+ -> P.PathsPrefixTree Behave AnIssue a
304− (Report, P.PathsPrefixTree Behave AnIssue a)
251+ -> (Report, P.PathsPrefixTree Behave AnIssue a)
305observeJetShowErrs jet p = case observeJetShowErrs' jet p of
252observeJetShowErrs jet p = case observeJetShowErrs' jet p of
306 Just m -> m
253 Just m -> m
307 Nothing -> (mempty, p)
254 Nothing -> (mempty, p)
308
255
309−observeJetShowErrs' ::
256+observeJetShowErrs'
310− forall a.
257+ :: forall a.
311− ReportJet' Behave (Maybe Inlines) ->
258+ ReportJet' Behave (Maybe Inlines)
312− P.PathsPrefixTree Behave AnIssue a ->
259+ -> P.PathsPrefixTree Behave AnIssue a
313− Maybe (Report, P.PathsPrefixTree Behave AnIssue a)
260+ -> Maybe (Report, P.PathsPrefixTree Behave AnIssue a)
314observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues) =
261observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues) =
315 let results =
262 let results =
316 subIssues >>= \(WrapTypeable (AStep m)) ->
263 subIssues >>= \(WrapTypeable (AStep m)) ->
@@ -318,23 +265,21 @@ observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues)
318 maybe (Left $ embed (step bhv) subErrs) Right . listToMaybe $
265 maybe (Left $ embed (step bhv) subErrs) Right . listToMaybe $
319 jet @_ @_ @[] bhv
266 jet @_ @_ @[] bhv
320 & mapMaybe
267 & mapMaybe
321− ( \case
268+ (\case
322− Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
269+ Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
323− Pure (Just h) ->
270+ Pure (Just h) ->
324− if P.null subErrs
271+ if P.null subErrs
325− then Just mempty
272+ then Just mempty
326− else Just (singletonHeader h (showErrs subErrs), mempty)
273+ else Just (singletonHeader h (showErrs subErrs), mempty)
327− Pure Nothing -> Nothing
274+ Pure Nothing -> Nothing)
328− )
329 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
275 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
330 if any isRight results
276 if any isRight results
331 then
277 then
332 Just $
278 Just $
333 foldMap
279 foldMap
334− ( \case
280+ (\case
335− Left e -> (mempty, e)
281+ Left e -> (mempty, e)
336− Right m -> m
282+ Right m -> m)
337− )
338 results
283 results
339 else Nothing
284 else Nothing
340
285
@@ -357,9 +302,8 @@ buildReport cfg = case treeStyle cfg of
357 body rprt
302 body rprt
358 <> foldOMapWithKey
303 <> foldOMapWithKey
359 (headers rprt)
304 (headers rprt)
360− ( \k v ->
305+ (\k v ->
361− header level k <> subBuilder v
306+ header level k <> subBuilder v)
362− )
363 where
307 where
364 subBuilder = headerStyleBuilder (level + 1)
308 subBuilder = headerStyleBuilder (level + 1)
365
309
@@ -368,17 +312,16 @@ buildReport cfg = case treeStyle cfg of
368 body rprt
312 body rprt
369 <> foldOMapWithKey
313 <> foldOMapWithKey
370 (headers rprt)
314 (headers rprt)
371− ( \k v ->
315+ (\k v ->
372− if (OM.size . headers $ rprt) < 2
316+ if (OM.size . headers $ rprt) < 2
373− then para k <> blockQuote (subBuilder v)
317+ then para k <> blockQuote (subBuilder v)
374− else
318+ else
375− rawHtml "<details>"
319+ rawHtml "<details>"
376− <> rawHtml "<summary>"
320+ <> rawHtml "<summary>"
377− <> plain k
321+ <> plain k
378− <> rawHtml "</summary>"
322+ <> rawHtml "</summary>"
379− <> blockQuote (subBuilder v)
323+ <> blockQuote (subBuilder v)
380− <> rawHtml "</details>"
324+ <> rawHtml "</details>")
381− )
382 where
325 where
383 subBuilder = foldingStyleBuilder
326 subBuilder = foldingStyleBuilder
384
327
src/Data/OpenApi/Compare/Report/Html/Template.hs → src/OpenAPI/Checker/Report/Html/Template.hsrenamed+5−5
@@ -1,7 +1,7 @@
1{-# LANGUAGE TemplateHaskell #-}
1{-# LANGUAGE TemplateHaskell #-}
2
2
3−module Data.OpenApi.Compare.Report.Html.Template
3+module OpenAPI.Checker.Report.Html.Template
4− ( template,
4+ ( template
5 )
5 )
6where
6where
7
7
@@ -24,11 +24,11 @@ template =
24 \<meta charset=\"utf-8\">\
24 \<meta charset=\"utf-8\">\
25 \<title></title>\
25 \<title></title>\
26 \<meta name=\"description\" content=\"\">\
26 \<meta name=\"description\" content=\"\">\
27− \<meta name=\"generator\" content=\"CompaREST\" />\
27+ \<meta name=\"generator\" content=\"OpenApi Diff\" />\
28 \<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\
28 \<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\" />\
29 \</head>\
29 \</head>\
30 \<body>\
30 \<body>\
31− \<header><h1>CompaREST</h1></header>\
31+ \<header><h1>⛄ OpenApi Diff</h1></header>\
32 \<main>\
32 \<main>\
33 \$body$\
33 \$body$\
34 \</main>\
34 \</main>\
@@ -36,4 +36,4 @@ template =
36 \</html>"
36 \</html>"
37
37
38awsmCss :: ByteString
38awsmCss :: 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
1+module OpenAPI.Checker.Report.Jet
2− ( ReportJet (..),
2+ ( ReportJet (..)
3− ReportJet',
3+ , ReportJet'
4− ConstructReportJet (..),
4+ , ConstructReportJet (..)
5− ReportJetResult,
5+ , ReportJetResult
6 )
6 )
7where
7where
8
8
@@ -23,7 +23,7 @@ import Text.Pandoc.Builder
23-- The term "jet" in this context was introduced in the Urbit project:
23-- The term "jet" in this context was introduced in the Urbit project:
24-- https://urbit.org/docs/vere/jetting/
24-- https://urbit.org/docs/vere/jetting/
25--
25--
26−-- The pattern fits well for simplifying 'Behavior' tree paths.
26+-- The pattern fits well for simplifying 'Behaviour' tree paths.
27class ConstructReportJet x f where
27class ConstructReportJet x f where
28 constructReportJet :: x -> ReportJetResult f (Maybe Inlines)
28 constructReportJet :: x -> ReportJetResult f (Maybe Inlines)
29
29
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+126−128
@@ -1,52 +1,52 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Subtree
3+module OpenAPI.Checker.Subtree
4− ( Steppable (..),
4+ ( Steppable (..)
5− Step (..),
5+ , Step (..)
6− TraceRoot,
6+ , TraceRoot
7− Trace,
7+ , Trace
8− Traced,
8+ , Traced
9− Traced',
9+ , Traced'
10− pattern Traced,
10+ , pattern Traced
11− traced,
11+ , traced
12− retraced,
12+ , retraced
13− stepTraced,
13+ , stepTraced
14− Subtree (..),
14+ , Subtree (..)
15− checkCompatibility,
15+ , checkCompatibility
16− checkSubstructure,
16+ , checkSubstructure
17− CompatM (..),
17+ , CompatM (..)
18− CompatFormula',
18+ , CompatFormula'
19− SemanticCompatFormula,
19+ , SemanticCompatFormula
20− ProdCons (..),
20+ , ProdCons (..)
21− orientProdCons,
21+ , orientProdCons
22− swapProdCons,
22+ , swapProdCons
23− runCompatFormula,
23+ , runCompatFormula
24− issueAt,
24+ , issueAt
25− anItem,
25+ , anItem
26− anIssue,
26+ , anIssue
27− invertIssueOrientation,
27+ , invertIssueOrientation
28− invertIssueOrientationP,
28+ , invertIssueOrientationP
29− embedFormula,
29+ , embedFormula
30− anyOfAt,
30+ , anyOfAt
31− clarifyIssue,
31+ , clarifyIssue
32− structuralIssue,
32+ , structuralIssue
33
33
34 -- * Structural helpers
34 -- * Structural helpers
35− structuralMaybe,
35+ , structuralMaybe
36− structuralMaybeWith,
36+ , structuralMaybeWith
37− structuralEq,
37+ , structuralEq
38− iohmStructural,
38+ , iohmStructural
39− iohmStructuralWith,
39+ , iohmStructuralWith
40− structuralList,
40+ , structuralList
41
41
42 -- * Reexports
42 -- * Reexports
43− (>>>),
43+ , (>>>)
44− (<<<),
44+ , (<<<)
45− extract,
45+ , extract
46− ask,
46+ , ask
47− local,
47+ , local
48− step,
48+ , step
49− Typeable,
49+ , Typeable
50 )
50 )
51where
51where
52
52
@@ -60,13 +60,13 @@ import qualified Data.HashMap.Strict.InsOrd as IOHM
60import Data.Hashable
60import Data.Hashable
61import Data.Kind
61import Data.Kind
62import Data.OpenApi
62import 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
68import qualified Data.Set as S
63import qualified Data.Set as S
69import Data.Typeable
64import Data.Typeable
65+import OpenAPI.Checker.Behavior
66+import OpenAPI.Checker.Formula
67+import OpenAPI.Checker.Memo
68+import OpenAPI.Checker.Paths
69+import qualified OpenAPI.Checker.PathsPrefixTree as P
70
70
71class
71class
72 (Typeable Step, Typeable a, Typeable b, Ord (Step a b), Show (Step a b)) =>
72 (Typeable Step, Typeable a, Typeable b, Ord (Step a b), Show (Step a b)) =>
@@ -115,10 +115,10 @@ orientProdCons :: Orientation -> ProdCons x -> ProdCons x
115orientProdCons Forward x = x
115orientProdCons Forward x = x
116orientProdCons Backward (ProdCons p c) = ProdCons c p
116orientProdCons Backward (ProdCons p c) = ProdCons c p
117
117
118−swapProdCons ::
118+swapProdCons
119− SwapEnvRoles xs =>
119+ :: SwapEnvRoles xs
120− (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a) ->
120+ => (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
121− (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
121+ -> (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
122swapProdCons f e (ProdCons p c) =
122swapProdCons f e (ProdCons p c) =
123 invertIssueOrientation $
123 invertIssueOrientation $
124 f (swapEnvRoles e) (ProdCons c p)
124 f (swapEnvRoles e) (ProdCons c p)
@@ -153,8 +153,8 @@ instance Applicative ProdCons where
153 ProdCons fp fc <*> ProdCons xp xc = ProdCons (fp xp) (fc xc)
153 ProdCons fp fc <*> ProdCons xp xc = ProdCons (fp xp) (fc xc)
154
154
155newtype CompatM a = CompatM
155newtype CompatM a = CompatM
156− { unCompatM ::
156+ { unCompatM
157− StateT (MemoState VarRef) Identity a
157+ :: StateT (MemoState VarRef) Identity a
158 }
158 }
159 deriving newtype
159 deriving newtype
160 ( Functor
160 ( Functor
@@ -183,64 +183,64 @@ class (Typeable t, Issuable (SubtreeLevel t)) => Subtree (t :: Type) where
183 type CheckEnv t :: [Type]
183 type CheckEnv t :: [Type]
184 type SubtreeLevel t :: BehaviorLevel
184 type SubtreeLevel t :: BehaviorLevel
185
185
186− checkStructuralCompatibility ::
186+ checkStructuralCompatibility
187− HList (CheckEnv t) ->
187+ :: HList (CheckEnv t)
188− ProdCons (Traced t) ->
188+ -> ProdCons (Traced t)
189− StructuralCompatFormula ()
189+ -> StructuralCompatFormula ()
190
190
191− checkSemanticCompatibility ::
191+ checkSemanticCompatibility
192− HList (CheckEnv t) ->
192+ :: HList (CheckEnv t)
193− Behavior (SubtreeLevel t) ->
193+ -> Behavior (SubtreeLevel t)
194− ProdCons (Traced t) ->
194+ -> ProdCons (Traced t)
195− SemanticCompatFormula ()
195+ -> SemanticCompatFormula ()
196
196
197{-# WARNING checkStructuralCompatibility "You should not be calling this directly. Use 'checkSubstructure'" #-}
197{-# WARNING checkStructuralCompatibility "You should not be calling this directly. Use 'checkSubstructure'" #-}
198
198
199{-# WARNING checkSemanticCompatibility "You should not be calling this directly. Use 'checkCompatibility'" #-}
199{-# WARNING checkSemanticCompatibility "You should not be calling this directly. Use 'checkCompatibility'" #-}
200
200
201−checkCompatibility ::
201+checkCompatibility
202− forall t xs.
202+ :: forall t xs.
203− (ReassembleHList xs (CheckEnv t), Subtree t) =>
203+ (ReassembleHList xs (CheckEnv t), Subtree t)
204− Behavior (SubtreeLevel t) ->
204+ => Behavior (SubtreeLevel t)
205− HList xs ->
205+ -> HList xs
206− ProdCons (Traced t) ->
206+ -> ProdCons (Traced t)
207− SemanticCompatFormula ()
207+ -> SemanticCompatFormula ()
208checkCompatibility bhv e = memo bhv SemanticMemoKey $ \pc ->
208checkCompatibility bhv e = memo bhv SemanticMemoKey $ \pc ->
209 case runCompatFormula $ checkSubstructure e pc of
209 case runCompatFormula $ checkSubstructure e pc of
210 Left _ -> checkSemanticCompatibility (reassemble e) bhv pc
210 Left _ -> checkSemanticCompatibility (reassemble e) bhv pc
211 Right () -> pure ()
211 Right () -> pure ()
212{-# INLINE checkCompatibility #-}
212{-# INLINE checkCompatibility #-}
213
213
214−checkSubstructure ::
214+checkSubstructure
215− (ReassembleHList xs (CheckEnv t), Subtree t) =>
215+ :: (ReassembleHList xs (CheckEnv t), Subtree t)
216− HList xs ->
216+ => HList xs
217− ProdCons (Traced t) ->
217+ -> ProdCons (Traced t)
218− StructuralCompatFormula ()
218+ -> StructuralCompatFormula ()
219checkSubstructure e = memo Root StructuralMemoKey $ checkStructuralCompatibility (reassemble e)
219checkSubstructure e = memo Root StructuralMemoKey $ checkStructuralCompatibility (reassemble e)
220{-# INLINE checkSubstructure #-}
220{-# INLINE checkSubstructure #-}
221
221
222−structuralMaybe ::
222+structuralMaybe
223− (Subtree a, ReassembleHList xs (CheckEnv a)) =>
223+ :: (Subtree a, ReassembleHList xs (CheckEnv a))
224− HList xs ->
224+ => HList xs
225− ProdCons (Maybe (Traced a)) ->
225+ -> ProdCons (Maybe (Traced a))
226− StructuralCompatFormula ()
226+ -> StructuralCompatFormula ()
227structuralMaybe e = structuralMaybeWith (checkSubstructure e)
227structuralMaybe e = structuralMaybeWith (checkSubstructure e)
228{-# INLINE structuralMaybe #-}
228{-# INLINE structuralMaybe #-}
229
229
230−structuralMaybeWith ::
230+structuralMaybeWith
231− (ProdCons a -> StructuralCompatFormula ()) ->
231+ :: (ProdCons a -> StructuralCompatFormula ())
232− ProdCons (Maybe a) ->
232+ -> ProdCons (Maybe a)
233− StructuralCompatFormula ()
233+ -> StructuralCompatFormula ()
234structuralMaybeWith f (ProdCons (Just a) (Just b)) = f $ ProdCons a b
234structuralMaybeWith f (ProdCons (Just a) (Just b)) = f $ ProdCons a b
235structuralMaybeWith _ (ProdCons Nothing Nothing) = pure ()
235structuralMaybeWith _ (ProdCons Nothing Nothing) = pure ()
236structuralMaybeWith _ _ = structuralIssue
236structuralMaybeWith _ _ = structuralIssue
237{-# INLINE structuralMaybeWith #-}
237{-# INLINE structuralMaybeWith #-}
238
238
239−structuralList ::
239+structuralList
240− (Subtree a, ReassembleHList xs (CheckEnv a)) =>
240+ :: (Subtree a, ReassembleHList xs (CheckEnv a))
241− HList xs ->
241+ => HList xs
242− ProdCons [Traced a] ->
242+ -> ProdCons [Traced a]
243− StructuralCompatFormula ()
243+ -> StructuralCompatFormula ()
244structuralList _ (ProdCons [] []) = pure ()
244structuralList _ (ProdCons [] []) = pure ()
245structuralList e (ProdCons (a : aa) (b : bb)) = do
245structuralList e (ProdCons (a : aa) (b : bb)) = do
246 checkSubstructure e $ ProdCons a b
246 checkSubstructure e $ ProdCons a b
@@ -253,11 +253,11 @@ structuralEq :: (Eq a, Comonad w) => ProdCons (w a) -> StructuralCompatFormula (
253structuralEq (ProdCons a b) = if extract a == extract b then pure () else structuralIssue
253structuralEq (ProdCons a b) = if extract a == extract b then pure () else structuralIssue
254{-# INLINE structuralEq #-}
254{-# INLINE structuralEq #-}
255
255
256−iohmStructural ::
256+iohmStructural
257− (ReassembleHList (k ': xs) (CheckEnv v), Ord k, Subtree v, Hashable k, Typeable k, Show k) =>
257+ :: (ReassembleHList (k ': xs) (CheckEnv v), Ord k, Subtree v, Hashable k, Typeable k, Show k)
258− HList xs ->
258+ => HList xs
259− ProdCons (Traced (IOHM.InsOrdHashMap k v)) ->
259+ -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
260− StructuralCompatFormula ()
260+ -> StructuralCompatFormula ()
261iohmStructural e =
261iohmStructural e =
262 iohmStructuralWith (\k -> checkSubstructure (k `HCons` e))
262 iohmStructuralWith (\k -> checkSubstructure (k `HCons` e))
263{-# INLINE iohmStructural #-}
263{-# INLINE iohmStructural #-}
@@ -266,26 +266,25 @@ instance (Typeable k, Typeable v, Ord k, Show k) => Steppable (IOHM.InsOrdHashMa
266 data Step (IOHM.InsOrdHashMap k v) v = InsOrdHashMapKeyStep k
266 data Step (IOHM.InsOrdHashMap k v) v = InsOrdHashMapKeyStep k
267 deriving stock (Eq, Ord, Show)
267 deriving stock (Eq, Ord, Show)
268
268
269−iohmStructuralWith ::
269+iohmStructuralWith
270− (Ord k, Hashable k, Typeable k, Typeable v, Show k) =>
270+ :: (Ord k, Hashable k, Typeable k, Typeable v, Show k)
271− (k -> ProdCons (Traced v) -> StructuralCompatFormula ()) ->
271+ => (k -> ProdCons (Traced v) -> StructuralCompatFormula ())
272− ProdCons (Traced (IOHM.InsOrdHashMap k v)) ->
272+ -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
273− StructuralCompatFormula ()
273+ -> StructuralCompatFormula ()
274iohmStructuralWith f pc = do
274iohmStructuralWith f pc = do
275 let ProdCons pEKeys cEKeys = S.fromList . IOHM.keys . extract <$> pc
275 let ProdCons pEKeys cEKeys = S.fromList . IOHM.keys . extract <$> pc
276 if pEKeys == cEKeys
276 if pEKeys == cEKeys
277 then
277 then
278 for_
278 for_
279 pEKeys
279 pEKeys
280− ( \eKey ->
280+ (\eKey ->
281− f eKey $ stepTraced (InsOrdHashMapKeyStep eKey) . fmap (IOHM.lookupDefault (error "impossible") eKey) <$> pc
281+ f eKey $ stepTraced (InsOrdHashMapKeyStep eKey) . fmap (IOHM.lookupDefault (error "impossible") eKey) <$> pc)
282− )
283 else structuralIssue
282 else structuralIssue
284{-# INLINE iohmStructuralWith #-}
283{-# INLINE iohmStructuralWith #-}
285
284
286−runCompatFormula ::
285+runCompatFormula
287− CompatFormula' q f r a ->
286+ :: CompatFormula' q f r a
288− Either (P.PathsPrefixTree q f r) a
287+ -> Either (P.PathsPrefixTree q f r) a
289runCompatFormula (Compose f) =
288runCompatFormula (Compose f) =
290 calculate . runIdentity . runMemo 0 . unCompatM $ f
289 calculate . runIdentity . runMemo 0 . unCompatM $ f
291{-# INLINE runCompatFormula #-}
290{-# INLINE runCompatFormula #-}
@@ -315,27 +314,27 @@ invertIssueOrientationP = P.map (\(AnIssue ori i) -> AnIssue (toggleOrientation
315structuralIssue :: StructuralCompatFormula a
314structuralIssue :: StructuralCompatFormula a
316structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
315structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
317
316
318−anyOfAt ::
317+anyOfAt
319− Issuable l =>
318+ :: Issuable l
320− Paths q r l ->
319+ => Paths q r l
321− Issue l ->
320+ -> Issue l
322− [CompatFormula' q AnIssue r a] ->
321+ -> [CompatFormula' q AnIssue r a]
323− CompatFormula' q AnIssue r a
322+ -> CompatFormula' q AnIssue r a
324anyOfAt _ _ [x] = x
323anyOfAt _ _ [x] = x
325anyOfAt xs issue fs =
324anyOfAt xs issue fs =
326 Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> traverse getCompose fs
325 Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> traverse getCompose fs
327
326
328-- | If the given formula contains any issues, add another issue on top. Otherwise succeed.
327-- | If the given formula contains any issues, add another issue on top. Otherwise succeed.
329−clarifyIssue ::
328+clarifyIssue
330− AnItem q AnIssue r ->
329+ :: AnItem q AnIssue r
331− CompatFormula' q AnIssue r a ->
330+ -> CompatFormula' q AnIssue r a
332− CompatFormula' q AnIssue r a
331+ -> CompatFormula' q AnIssue r a
333clarifyIssue item f =
332clarifyIssue item f =
334 Compose ((`eitherOf` item) <$> pure <$> getCompose f) *> f
333 Compose ((`eitherOf` item) <$> pure <$> getCompose f) *> f
335
334
336−fixpointKnot ::
335+fixpointKnot
337− MonadState (MemoState VarRef) m =>
336+ :: MonadState (MemoState VarRef) m
338− KnotTier (FormulaF q f r ()) VarRef m
337+ => KnotTier (FormulaF q f r ()) VarRef m
339fixpointKnot =
338fixpointKnot =
340 KnotTier
339 KnotTier
341 { onKnotFound = modifyMemoNonce succ
340 { onKnotFound = modifyMemoNonce succ
@@ -343,20 +342,19 @@ fixpointKnot =
343 , tieKnot = \i x -> pure $ maxFixpoint i x
342 , tieKnot = \i x -> pure $ maxFixpoint i x
344 }
343 }
345
344
346−memo ::
345+memo
347− (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a) =>
346+ :: (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a)
348− Paths q r l ->
347+ => Paths q r l
349− MemoKey ->
348+ -> MemoKey
350− (ProdCons (Traced a) -> CompatFormula' q f r ()) ->
349+ -> (ProdCons (Traced a) -> CompatFormula' q f r ())
351− (ProdCons (Traced a) -> CompatFormula' q f r ())
350+ -> (ProdCons (Traced a) -> CompatFormula' q f r ())
352memo bhv k f pc = Compose $ do
351memo bhv k f pc = Compose $ do
353 formula' <-
352 formula' <-
354 memoWithKnot
353 memoWithKnot
355 fixpointKnot
354 fixpointKnot
356− ( do
355+ (do
357− formula <- getCompose $ f pc
356+ formula <- getCompose $ f pc
358− pure $ mapErrors (P.takeSubtree bhv) formula
357+ pure $ mapErrors (P.takeSubtree bhv) formula)
359− )
360 (k, ask <$> pc)
358 (k, ask <$> pc)
361 pure $ mapErrors (P.embed bhv) formula'
359 pure $ mapErrors (P.embed bhv) formula'
362
360
src/Data/OpenApi/Compare/Validate/Header.hs → src/OpenAPI/Checker/Validate/Header.hsrenamed+7−9
@@ -1,6 +1,6 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.Header
3+module OpenAPI.Checker.Validate.Header
4 (
4 (
5 )
5 )
6where
6where
@@ -9,10 +9,10 @@ import Data.Foldable
9import Data.Functor
9import Data.Functor
10import Data.Maybe
10import Data.Maybe
11import Data.OpenApi
11import Data.OpenApi
12−import Data.OpenApi.Compare.Behavior
12+import OpenAPI.Checker.Behavior
13−import Data.OpenApi.Compare.References ()
13+import OpenAPI.Checker.References ()
14−import Data.OpenApi.Compare.Subtree
14+import OpenAPI.Checker.Subtree
15−import Data.OpenApi.Compare.Validate.Schema ()
15+import OpenAPI.Checker.Validate.Schema ()
16import Text.Pandoc.Builder
16import Text.Pandoc.Builder
17
17
18instance Subtree Header where
18instance Subtree Header where
@@ -50,9 +50,7 @@ instance Issuable 'HeaderLevel where
50 | NonEmptyHeaderRequired
50 | NonEmptyHeaderRequired
51 | HeaderSchemaRequired
51 | HeaderSchemaRequired
52 deriving stock (Eq, Ord, Show)
52 deriving stock (Eq, Ord, Show)
53− issueKind = \case
53+ issueIsUnsupported _ = False
54− HeaderSchemaRequired -> ProbablyIssue -- catch-all schema?
55− _ -> CertainIssue
56 describeIssue Forward RequiredHeaderMissing = para "Header has become required."
54 describeIssue Forward RequiredHeaderMissing = para "Header has become required."
57 describeIssue Backward RequiredHeaderMissing = para "Header is no longer required."
55 describeIssue Backward RequiredHeaderMissing = para "Header is no longer required."
58 describeIssue Forward NonEmptyHeaderRequired = para "The header does not allow empty values anymore."
56 describeIssue Forward NonEmptyHeaderRequired = para "The header does not allow empty values anymore."
@@ -63,4 +61,4 @@ instance Behavable 'HeaderLevel 'SchemaLevel where
63 data Behave 'HeaderLevel 'SchemaLevel
61 data Behave 'HeaderLevel 'SchemaLevel
64 = InSchema
62 = InSchema
65 deriving stock (Eq, Ord, Show)
63 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 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.Link () where
3+module OpenAPI.Checker.Validate.Link () where
4
4
5import Data.OpenApi
5import Data.OpenApi
6−import Data.OpenApi.Compare.Behavior
6+import OpenAPI.Checker.Behavior
7−import Data.OpenApi.Compare.Subtree
7+import OpenAPI.Checker.Subtree
8import Text.Pandoc.Builder
8import Text.Pandoc.Builder
9
9
10instance Subtree Link where
10instance Subtree Link where
@@ -17,6 +17,6 @@ instance Issuable 'LinkLevel where
17 data Issue 'LinkLevel
17 data Issue 'LinkLevel
18 = LinksUnsupported
18 = LinksUnsupported
19 deriving stock (Eq, Ord, Show)
19 deriving stock (Eq, Ord, Show)
20− issueKind = \case
20+ issueIsUnsupported = \case
21− LinksUnsupported -> Unsupported
21+ LinksUnsupported -> True
22− describeIssue _ LinksUnsupported = para "CompaREST does not currently support Link Objects."
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 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.MediaTypeObject
3+module OpenAPI.Checker.Validate.MediaTypeObject
4− ( Issue (..),
4+ ( Issue (..)
5− Behave (..),
5+ , Behave (..)
6 )
6 )
7where
7where
8
8
@@ -12,15 +12,15 @@ import Data.HList
12import Data.HashMap.Strict.InsOrd as IOHM
12import Data.HashMap.Strict.InsOrd as IOHM
13import Data.Map.Strict as M
13import Data.Map.Strict as M
14import Data.OpenApi
14import 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 ()
20import Data.String
15import Data.String
21import Data.Text (Text)
16import Data.Text (Text)
22import qualified Data.Text as T
17import qualified Data.Text as T
23import Network.HTTP.Media (MediaType, mainType, subType)
18import 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 ()
24import Text.Pandoc.Builder
24import Text.Pandoc.Builder
25
25
26tracedSchema :: Traced MediaTypeObject -> Maybe (Traced (Referenced Schema))
26tracedSchema :: Traced MediaTypeObject -> Maybe (Traced (Referenced Schema))
@@ -38,20 +38,20 @@ instance Issuable 'PayloadLevel where
38 | MediaEncodingMissing Text
38 | MediaEncodingMissing Text
39 | EncodingNotSupported
39 | EncodingNotSupported
40 deriving stock (Eq, Ord, Show)
40 deriving stock (Eq, Ord, Show)
41− issueKind = \case
41+ issueIsUnsupported = \case
42− EncodingNotSupported -> Unsupported
42+ EncodingNotSupported -> True
43− _ -> CertainIssue
43+ _ -> False
44
44
45 describeIssue _ MediaTypeSchemaRequired = para "Media type expected, but was not specified."
45 describeIssue _ MediaTypeSchemaRequired = para "Media type expected, but was not specified."
46 describeIssue Forward (MediaEncodingMissing enc) = para $ "Media encoding " <> str enc <> " has been removed."
46 describeIssue Forward (MediaEncodingMissing enc) = para $ "Media encoding " <> str enc <> " has been removed."
47 describeIssue Backward (MediaEncodingMissing enc) = para $ "Media encoding " <> str enc <> " added."
47 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."
49
49
50instance Behavable 'PayloadLevel 'SchemaLevel where
50instance Behavable 'PayloadLevel 'SchemaLevel where
51 data Behave 'PayloadLevel 'SchemaLevel
51 data Behave 'PayloadLevel 'SchemaLevel
52 = PayloadSchema
52 = PayloadSchema
53 deriving stock (Eq, Ord, Show)
53 deriving stock (Eq, Ord, Show)
54− describeBehavior PayloadSchema = "JSON Schema"
54+ describeBehaviour PayloadSchema = "JSON Schema"
55
55
56instance Subtree MediaTypeObject where
56instance Subtree MediaTypeObject where
57 type SubtreeLevel MediaTypeObject = 'PayloadLevel
57 type SubtreeLevel MediaTypeObject = 'PayloadLevel
@@ -119,7 +119,7 @@ instance Subtree Encoding where
119
119
120 -- FIXME: Support only JSON body for now. Then Encoding is checked only for
120 -- FIXME: Support only JSON body for now. Then Encoding is checked only for
121 -- multipart/form-url-encoded
121 -- multipart/form-url-encoded
122− -- #54
122+ -- https://github.com/typeable/openapi-diff/issues/54
123 checkSemanticCompatibility _env beh _pc =
123 checkSemanticCompatibility _env beh _pc =
124 issueAt beh EncodingNotSupported
124 issueAt beh EncodingNotSupported
125
125
@@ -143,7 +143,7 @@ instance Behavable 'OperationLevel 'ResponseLevel where
143 data Behave 'OperationLevel 'ResponseLevel
143 data Behave 'OperationLevel 'ResponseLevel
144 = WithStatusCode HttpStatusCode
144 = WithStatusCode HttpStatusCode
145 deriving stock (Eq, Ord, Show)
145 deriving stock (Eq, Ord, Show)
146− describeBehavior (WithStatusCode c) = "Response code " <> (fromString . show $ c)
146+ describeBehaviour (WithStatusCode c) = "Response code " <> (fromString . show $ c)
147
147
148instance Issuable 'OperationLevel where
148instance Issuable 'OperationLevel where
149 data Issue 'OperationLevel
149 data Issue 'OperationLevel
@@ -152,8 +152,8 @@ instance Issuable 'OperationLevel where
152 | PathFragmentNotMatched Int
152 | PathFragmentNotMatched Int
153 | NoRequestBody
153 | NoRequestBody
154 deriving stock (Eq, Ord, Show)
154 deriving stock (Eq, Ord, Show)
155− issueKind = \case
155+ issueIsUnsupported = \case
156− _ -> CertainIssue
156+ _ -> False
157 describeIssue Forward (ConsumerDoesntHaveResponseCode c) =
157 describeIssue Forward (ConsumerDoesntHaveResponseCode c) =
158 para $ "Response code " <> (str . T.pack . show $ c) <> " has been removed."
158 para $ "Response code " <> (str . T.pack . show $ c) <> " has been removed."
159 describeIssue Backward (ConsumerDoesntHaveResponseCode c) =
159 describeIssue Backward (ConsumerDoesntHaveResponseCode c) =
src/Data/OpenApi/Compare/Validate/OAuth2Flows.hs → src/OpenAPI/Checker/Validate/OAuth2Flows.hsrenamed+21−26
@@ -1,9 +1,9 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.OAuth2Flows
3+module OpenAPI.Checker.Validate.OAuth2Flows
4− ( Step (..),
4+ ( Step (..)
5− Issue (..),
5+ , Issue (..)
6− Behave (..),
6+ , Behave (..)
7 )
7 )
8where
8where
9
9
@@ -12,13 +12,13 @@ import Data.Function
12import Data.Functor
12import Data.Functor
13import qualified Data.HashMap.Strict.InsOrd as IOHM
13import qualified Data.HashMap.Strict.InsOrd as IOHM
14import Data.OpenApi
14import Data.OpenApi
15−import Data.OpenApi.Compare.Behavior
16−import Data.OpenApi.Compare.Orphans ()
17−import Data.OpenApi.Compare.Subtree
18import Data.Proxy
15import Data.Proxy
19import Data.Set (Set)
16import Data.Set (Set)
20import qualified Data.Set as S
17import qualified Data.Set as S
21import Data.Text (Text)
18import Data.Text (Text)
19+import OpenAPI.Checker.Behavior
20+import OpenAPI.Checker.Orphans ()
21+import OpenAPI.Checker.Subtree
22import Text.Pandoc.Builder
22import Text.Pandoc.Builder
23
23
24instance Subtree OAuth2Flows where
24instance Subtree OAuth2Flows where
@@ -26,11 +26,11 @@ instance Subtree OAuth2Flows where
26 type SubtreeLevel OAuth2Flows = 'SecuritySchemeLevel
26 type SubtreeLevel OAuth2Flows = 'SecuritySchemeLevel
27 checkStructuralCompatibility _ = structuralEq
27 checkStructuralCompatibility _ = structuralEq
28 checkSemanticCompatibility env bhv pc = do
28 checkSemanticCompatibility env bhv pc = do
29− let supportFlow ::
29+ let supportFlow
30− (Subtree t, SubtreeLevel t ~ SubtreeLevel OAuth2Flows, CheckEnv OAuth2Flows ~ CheckEnv t) =>
30+ :: (Subtree t, SubtreeLevel t ~ SubtreeLevel OAuth2Flows, CheckEnv OAuth2Flows ~ CheckEnv t)
31− Issue 'SecuritySchemeLevel ->
31+ => Issue 'SecuritySchemeLevel
32− ProdCons (Maybe (Traced t)) ->
32+ -> ProdCons (Maybe (Traced t))
33− SemanticCompatFormula ()
33+ -> SemanticCompatFormula ()
34 supportFlow i x = case x of
34 supportFlow i x = case x of
35 -- producer will not attempt this flow
35 -- producer will not attempt this flow
36 (ProdCons Nothing _) -> pure ()
36 (ProdCons Nothing _) -> pure ()
@@ -38,11 +38,11 @@ instance Subtree OAuth2Flows where
38 (ProdCons (Just _) Nothing) -> issueAt bhv i
38 (ProdCons (Just _) Nothing) -> issueAt bhv i
39 (ProdCons (Just p) (Just c)) ->
39 (ProdCons (Just p) (Just c)) ->
40 checkCompatibility bhv env $ ProdCons p c
40 checkCompatibility bhv env $ ProdCons p c
41− getFlow ::
41+ getFlow
42− Typeable x =>
42+ :: Typeable x
43− (OAuth2Flows -> Maybe (OAuth2Flow x)) ->
43+ => (OAuth2Flows -> Maybe (OAuth2Flow x))
44− Traced OAuth2Flows ->
44+ -> Traced OAuth2Flows
45− Maybe (Traced (OAuth2Flow x))
45+ -> Maybe (Traced (OAuth2Flow x))
46 getFlow f (Traced t a) = Traced (t >>> step (OAuth2FlowsFlow Proxy)) <$> f a
46 getFlow f (Traced t a) = Traced (t >>> step (OAuth2FlowsFlow Proxy)) <$> f a
47 supportFlow ConsumerDoesNotSupportImplicitFlow $ getFlow _oAuth2FlowsImplicit <$> pc
47 supportFlow ConsumerDoesNotSupportImplicitFlow $ getFlow _oAuth2FlowsImplicit <$> pc
48 supportFlow ConsumerDoesNotSupportPasswordFlow $ getFlow _oAuth2FlowsPassword <$> pc
48 supportFlow ConsumerDoesNotSupportPasswordFlow $ getFlow _oAuth2FlowsPassword <$> pc
@@ -103,9 +103,7 @@ instance Issuable 'SecurityRequirementLevel where
103 = SecurityRequirementNotMet
103 = SecurityRequirementNotMet
104 | UndefinedSecurityScheme Text
104 | UndefinedSecurityScheme Text
105 deriving stock (Eq, Ord, Show)
105 deriving stock (Eq, Ord, Show)
106− issueKind = \case
106+ issueIsUnsupported _ = False
107− SecurityRequirementNotMet -> CertainIssue
108− UndefinedSecurityScheme _ -> SchemaInvalid
109 describeIssue Forward SecurityRequirementNotMet = para "Security scheme has been removed."
107 describeIssue Forward SecurityRequirementNotMet = para "Security scheme has been removed."
110 describeIssue Backward SecurityRequirementNotMet = para "Security scheme was added."
108 describeIssue Backward SecurityRequirementNotMet = para "Security scheme was added."
111 describeIssue _ (UndefinedSecurityScheme k) = para $ "Security scheme " <> code k <> " is not defined."
109 describeIssue _ (UndefinedSecurityScheme k) = para $ "Security scheme " <> code k <> " is not defined."
@@ -131,14 +129,11 @@ instance Issuable 'SecuritySchemeLevel where
131 | CanNotHaveScopes
129 | CanNotHaveScopes
132 | ScopeNotDefined Text
130 | ScopeNotDefined Text
133 deriving stock (Eq, Ord, Show)
131 deriving stock (Eq, Ord, Show)
134− issueKind = \case
132+ issueIsUnsupported _ = False
135− CanNotHaveScopes -> SchemaInvalid
136− ScopeNotDefined _ -> SchemaInvalid
137− _ -> CertainIssue
138 describeIssue _ RefreshUrlsDontMatch = para "Refresh URL changed."
133 describeIssue _ RefreshUrlsDontMatch = para "Refresh URL changed."
139 describeIssue _ (HttpSchemeTypesDontMatch _ _) = para "HTTP scheme type changed."
134 describeIssue _ (HttpSchemeTypesDontMatch _ _) = para "HTTP scheme type changed."
140 describeIssue _ (ApiKeyParamsDontMatch _ _) = para "API Key parameters changed."
135 describeIssue _ (ApiKeyParamsDontMatch _ _) = para "API Key parameters changed."
141− describeIssue _ (OpenIdConnectUrlsDontMatch _ _) = para "OpenID Connect URL changed."
136+ describeIssue _ (OpenIdConnectUrlsDontMatch _ _) = para "OpenaId Connect URL changed."
142 describeIssue _ (CustomHttpSchemesDontMatch e a) =
137 describeIssue _ (CustomHttpSchemesDontMatch e a) =
143 para $ "Changed HTTP scheme from " <> code e <> " to " <> code a <> "."
138 para $ "Changed HTTP scheme from " <> code e <> " to " <> code a <> "."
144 describeIssue Forward ConsumerDoesNotSupportImplicitFlow = para "Implicit flow support has been removed."
139 describeIssue Forward ConsumerDoesNotSupportImplicitFlow = para "Implicit flow support has been removed."
@@ -167,4 +162,4 @@ instance Behavable 'SecurityRequirementLevel 'SecuritySchemeLevel where
167 data Behave 'SecurityRequirementLevel 'SecuritySchemeLevel
162 data Behave 'SecurityRequirementLevel 'SecuritySchemeLevel
168 = SecuritySchemeStep Text
163 = SecuritySchemeStep Text
169 deriving stock (Eq, Ord, Show)
164 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 @@
2-- Does not compiles otherwise
2-- Does not compiles otherwise
3{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
3{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
4
4
5−module Data.OpenApi.Compare.Validate.OpenApi
5+module OpenAPI.Checker.Validate.OpenApi
6− ( Behave (..),
6+ ( Behave (..)
7− Issue (..),
7+ , Issue (..)
8 )
8 )
9where
9where
10
10
11import Data.HList
11import Data.HList
12import qualified Data.HashMap.Strict.InsOrd as IOHM
12import qualified Data.HashMap.Strict.InsOrd as IOHM
13import Data.OpenApi
13import Data.OpenApi
14−import Data.OpenApi.Compare.Behavior
14+import OpenAPI.Checker.Behavior
15−import Data.OpenApi.Compare.Paths
15+import OpenAPI.Checker.Paths
16−import Data.OpenApi.Compare.Subtree
16+import OpenAPI.Checker.Subtree
17−import Data.OpenApi.Compare.Validate.Operation
17+import OpenAPI.Checker.Validate.Operation
18
18
19tracedPaths :: Traced OpenApi -> Traced ProcessedPathItems
19tracedPaths :: Traced OpenApi -> Traced ProcessedPathItems
20tracedPaths oa =
20tracedPaths oa =
@@ -38,8 +38,7 @@ tracedSecuritySchemes :: Traced OpenApi -> Traced (Definitions SecurityScheme)
38tracedSecuritySchemes oa =
38tracedSecuritySchemes oa =
39 traced
39 traced
40 (ask oa >>> step ComponentsSecurityScheme)
40 (ask oa >>> step ComponentsSecurityScheme)
41− (unSecurityDefinitions . _componentsSecuritySchemes . _openApiComponents . extract $ oa)
41+ (_componentsSecuritySchemes . _openApiComponents . extract $ oa)
42− where unSecurityDefinitions (SecurityDefinitions ds) = ds
43
42
44tracedResponses :: Traced OpenApi -> Traced (Definitions Response)
43tracedResponses :: Traced OpenApi -> Traced (Definitions Response)
45tracedResponses oa =
44tracedResponses oa =
@@ -80,17 +79,16 @@ instance Subtree OpenApi where
80 checkSemanticCompatibility _ beh prodCons = do
79 checkSemanticCompatibility _ beh prodCons = do
81 checkCompatibility @ProcessedPathItems
80 checkCompatibility @ProcessedPathItems
82 beh
81 beh
83− ( (tracedRequestBodies <$> prodCons)
82+ ((tracedRequestBodies <$> prodCons)
84− `HCons` (tracedParameters <$> prodCons)
83+ `HCons` (tracedParameters <$> prodCons)
85− `HCons` (tracedSecuritySchemes <$> prodCons)
84+ `HCons` (tracedSecuritySchemes <$> prodCons)
86− `HCons` (tracedResponses <$> prodCons)
85+ `HCons` (tracedResponses <$> prodCons)
87− `HCons` (tracedHeaders <$> prodCons)
86+ `HCons` (tracedHeaders <$> prodCons)
88− `HCons` (tracedSchemas <$> prodCons)
87+ `HCons` (tracedSchemas <$> prodCons)
89− `HCons` (_openApiServers . extract <$> prodCons)
88+ `HCons` (_openApiServers . extract <$> prodCons)
90− `HCons` (tracedLinks <$> prodCons)
89+ `HCons` (tracedLinks <$> prodCons)
91− `HCons` (tracedCallbacks <$> prodCons)
90+ `HCons` (tracedCallbacks <$> prodCons)
92− `HCons` HNil
91+ `HCons` HNil)
93− )
94 (tracedPaths <$> prodCons)
92 (tracedPaths <$> prodCons)
95
93
96instance Steppable OpenApi ProcessedPathItems where
94instance Steppable OpenApi ProcessedPathItems where
src/Data/OpenApi/Compare/Validate/Operation.hs → src/OpenAPI/Checker/Validate/Operation.hsrenamed+50−57
@@ -2,19 +2,19 @@
2{-# OPTIONS_GHC -Wno-orphans #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
3{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
3{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
4
4
5−module Data.OpenApi.Compare.Validate.Operation
5+module OpenAPI.Checker.Validate.Operation
6 ( -- * Operation
6 ( -- * Operation
7− MatchedOperation (..),
7+ MatchedOperation (..)
8− OperationMethod (..),
8+ , OperationMethod (..)
9− pathItemMethod,
9+ , pathItemMethod
10
10
11 -- * ProcessedPathItem
11 -- * ProcessedPathItem
12− ProcessedPathItem (..),
12+ , ProcessedPathItem (..)
13− ProcessedPathItems (..),
13+ , ProcessedPathItems (..)
14− processPathItems,
14+ , processPathItems
15− Step (..),
15+ , Step (..)
16− Behave (..),
16+ , Behave (..)
17− Issue (..),
17+ , Issue (..)
18 )
18 )
19where
19where
20
20
@@ -29,21 +29,21 @@ import qualified Data.List as L
29import Data.Map.Strict as M
29import Data.Map.Strict as M
30import Data.Maybe
30import Data.Maybe
31import Data.OpenApi
31import 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
44import Data.String
32import Data.String
45import Data.Text (Text)
33import Data.Text (Text)
46import qualified Data.Text as T
34import 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
47import Text.Pandoc.Builder
47import Text.Pandoc.Builder
48
48
49data MatchedOperation = MatchedOperation
49data MatchedOperation = MatchedOperation
@@ -86,12 +86,11 @@ tracedCallbacks (Traced t oper) =
86 | (k, v) <- IOHM.toList . _operationCallbacks . operation $ oper
86 | (k, v) <- IOHM.toList . _operationCallbacks . operation $ oper
87 ]
87 ]
88
88
89−-- FIXME: #28
89+-- FIXME: https://github.com/typeable/openapi-diff/issues/28
90−getServers ::
90+getServers
91− -- | Servers from env
91+ :: [Server] -- ^ Servers from env
92− [Server] ->
93− MatchedOperation ->
92+ -> MatchedOperation
94− [Server]
93+ -> [Server]
95getServers env oper =
94getServers env oper =
96 case _operationServers . operation $ oper of
95 case _operationServers . operation $ oper of
97 [] -> env
96 [] -> env
@@ -102,21 +101,21 @@ instance Behavable 'OperationLevel 'PathFragmentLevel where
102 = InParam Text
101 = InParam Text
103 | InFragment (PathFragment Text)
102 | InFragment (PathFragment Text)
104 deriving stock (Eq, Ord, Show)
103 deriving stock (Eq, Ord, Show)
105− describeBehavior (InParam p) = "Parameter " <> text p
104+ describeBehaviour (InParam p) = "Parameter " <> text p
106− describeBehavior (InFragment (StaticPath p)) = "Static fragment " <> code p
105+ describeBehaviour (InFragment (StaticPath p)) = "Static fragment " <> code p
107− describeBehavior (InFragment (DynamicPath p)) = "Dynamic fragment " <> code p
106+ describeBehaviour (InFragment (DynamicPath p)) = "Dynamic fragment " <> code p
108
107
109instance Behavable 'OperationLevel 'RequestLevel where
108instance Behavable 'OperationLevel 'RequestLevel where
110 data Behave 'OperationLevel 'RequestLevel
109 data Behave 'OperationLevel 'RequestLevel
111 = InRequest
110 = InRequest
112 deriving stock (Eq, Ord, Show)
111 deriving stock (Eq, Ord, Show)
113− describeBehavior InRequest = "Request"
112+ describeBehaviour InRequest = "Request"
114
113
115instance Behavable 'OperationLevel 'SecurityRequirementLevel where
114instance Behavable 'OperationLevel 'SecurityRequirementLevel where
116 data Behave 'OperationLevel 'SecurityRequirementLevel
115 data Behave 'OperationLevel 'SecurityRequirementLevel
117 = SecurityRequirementStep Int
116 = SecurityRequirementStep Int
118 deriving stock (Eq, Ord, Show)
117 deriving stock (Eq, Ord, Show)
119− describeBehavior (SecurityRequirementStep i) =
118+ describeBehaviour (SecurityRequirementStep i) =
120 "Security requirement " <> (text . T.pack . show $ i)
119 "Security requirement " <> (text . T.pack . show $ i)
121
120
122instance Subtree MatchedOperation where
121instance Subtree MatchedOperation where
@@ -242,7 +241,7 @@ instance Subtree MatchedOperation where
242 checkProducts beh (const NoRequestBody) (const check) elements
241 checkProducts beh (const NoRequestBody) (const check) elements
243 checkResponses =
242 checkResponses =
244 swapProdCons (checkCompatibility beh) env $ tracedResponses <$> prodCons
243 swapProdCons (checkCompatibility beh) env $ tracedResponses <$> prodCons
245− -- FIXME: #27
244+ -- FIXME: https://github.com/typeable/openapi-diff/issues/27
246 checkCallbacks = do
245 checkCallbacks = do
247 let ProdCons pCallbacks cCallbacks = tracedCallbacks <$> prodCons
246 let ProdCons pCallbacks cCallbacks = tracedCallbacks <$> prodCons
248 for_ pCallbacks $ \(k, pCallback) -> do
247 for_ pCallbacks $ \(k, pCallback) -> do
@@ -251,7 +250,7 @@ instance Subtree MatchedOperation where
251 cCallbacks <&> \(_, cCallback) -> do
250 cCallbacks <&> \(_, cCallback) -> do
252 swapProdCons (checkCompatibility beh') env $ ProdCons pCallback cCallback
251 swapProdCons (checkCompatibility beh') env $ ProdCons pCallback cCallback
253 pure ()
252 pure ()
254− -- FIXME: #28
253+ -- FIXME: https://github.com/typeable/openapi-diff/issues/28
255 checkOperationSecurity = do
254 checkOperationSecurity = do
256 let ProdCons pSecs cSecs = tracedSecurity <$> prodCons
255 let ProdCons pSecs cSecs = tracedSecurity <$> prodCons
257 for_ pSecs $ \(i, pSec) -> do
256 for_ pSecs $ \(i, pSec) -> do
@@ -336,12 +335,7 @@ instance Issuable 'APILevel where
336 | AllPathsFailed FilePath
335 | AllPathsFailed FilePath
337 -- When several paths match given but all checks failed
336 -- When several paths match given but all checks failed
338 deriving stock (Eq, Ord, Show)
337 deriving stock (Eq, Ord, Show)
339− issueKind = \case
338+ issueIsUnsupported _ = False
340− _ -> CertainIssue
341− relatedIssues =
342− (==) `withClass` \case
343− NoPathsMatched fp -> Just fp
344− AllPathsFailed fp -> Just fp
345 describeIssue Forward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
339 describeIssue Forward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
346 describeIssue Backward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been added."
340 describeIssue Backward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been added."
347 describeIssue Forward (AllPathsFailed p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
341 describeIssue Forward (AllPathsFailed p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
@@ -352,7 +346,7 @@ instance Behavable 'APILevel 'PathLevel where
352 = AtPath FilePath
346 = AtPath FilePath
353 deriving stock (Eq, Ord, Show)
347 deriving stock (Eq, Ord, Show)
354
348
355− describeBehavior (AtPath p) = str (T.pack p)
349+ describeBehaviour (AtPath p) = str (T.pack p)
356
350
357instance Subtree ProcessedPathItems where
351instance Subtree ProcessedPathItems where
358 type SubtreeLevel ProcessedPathItems = 'APILevel
352 type SubtreeLevel ProcessedPathItems = 'APILevel
@@ -436,18 +430,17 @@ tracedFragments mpi =
436 | (i, x) <- L.zip [0 ..] $ pathFragments $ extract mpi
430 | (i, x) <- L.zip [0 ..] $ pathFragments $ extract mpi
437 ]
431 ]
438
432
439−tracedMethod ::
433+tracedMethod
440− OperationMethod ->
434+ :: OperationMethod
441− Traced MatchedPathItem ->
435+ -> Traced MatchedPathItem
442− Maybe (Traced' MatchedOperation Operation)
436+ -> Maybe (Traced' MatchedOperation Operation)
443tracedMethod s mpi = env (ask mpi >>> step (OperationMethodStep s)) <$> (pathItemMethod s . pathItem . extract $ mpi)
437tracedMethod s mpi = env (ask mpi >>> step (OperationMethodStep s)) <$> (pathItemMethod s . pathItem . extract $ mpi)
444
438
445instance Issuable 'PathLevel where
439instance Issuable 'PathLevel where
446 data Issue 'PathLevel
440 data Issue 'PathLevel
447 = OperationMissing OperationMethod
441 = OperationMissing OperationMethod
448 deriving stock (Eq, Ord, Show)
442 deriving stock (Eq, Ord, Show)
449− issueKind = \case
443+ issueIsUnsupported _ = False
450− OperationMissing _ -> CertainIssue
451 describeIssue Forward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been removed."
444 describeIssue Forward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been removed."
452 describeIssue Backward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been added."
445 describeIssue Backward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been added."
453
446
@@ -456,7 +449,7 @@ instance Behavable 'PathLevel 'OperationLevel where
456 = InOperation OperationMethod
449 = InOperation OperationMethod
457 deriving stock (Eq, Ord, Show)
450 deriving stock (Eq, Ord, Show)
458
451
459− describeBehavior (InOperation method) = showMethod method
452+ describeBehaviour (InOperation method) = showMethod method
460
453
461showMethod :: IsString s => OperationMethod -> s
454showMethod :: IsString s => OperationMethod -> s
462showMethod = \case
455showMethod = \case
@@ -487,10 +480,10 @@ instance Subtree MatchedPathItem where
487 checkSemanticCompatibility env beh prodCons = do
480 checkSemanticCompatibility env beh prodCons = do
488 let paramDefs = getH @(ProdCons (Traced (Definitions Param))) env
481 let paramDefs = getH @(ProdCons (Traced (Definitions Param))) env
489 pathTracedParams = getPathParams <$> paramDefs <*> prodCons
482 pathTracedParams = getPathParams <$> paramDefs <*> prodCons
490− getPathParams ::
483+ getPathParams
491− Traced (Definitions Param) ->
484+ :: Traced (Definitions Param)
492− Traced MatchedPathItem ->
485+ -> Traced MatchedPathItem
493− [Traced Param]
486+ -> [Traced Param]
494 getPathParams defs mpi = do
487 getPathParams defs mpi = do
495 paramRef <- tracedMatchedPathItemParameters mpi
488 paramRef <- tracedMatchedPathItemParameters mpi
496 pure $ dereference defs paramRef
489 pure $ dereference defs paramRef
@@ -564,9 +557,9 @@ instance Issuable 'CallbackLevel where
564 data Issue 'CallbackLevel
557 data Issue 'CallbackLevel
565 = CallbacksUnsupported
558 = CallbacksUnsupported
566 deriving stock (Eq, Ord, Show)
559 deriving stock (Eq, Ord, Show)
567− issueKind = \case
560+ issueIsUnsupported = \case
568− CallbacksUnsupported -> Unsupported
561+ CallbacksUnsupported -> True
569− describeIssue _ CallbacksUnsupported = para "CompaREST does not currently support callbacks."
562+ describeIssue _ CallbacksUnsupported = para "OpenApi Diff does not currently support callbacks."
570
563
571tracedCallbackPathItems :: Traced Callback -> Traced ProcessedPathItems
564tracedCallbackPathItems :: Traced Callback -> Traced ProcessedPathItems
572tracedCallbackPathItems (Traced t (Callback x)) =
565tracedCallbackPathItems (Traced t (Callback x)) =
@@ -580,4 +573,4 @@ instance Behavable 'OperationLevel 'CallbackLevel where
580 data Behave 'OperationLevel 'CallbackLevel = OperationCallback Text
573 data Behave 'OperationLevel 'CallbackLevel = OperationCallback Text
581 deriving stock (Eq, Ord, Show)
574 deriving stock (Eq, Ord, Show)
582
575
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+14−18
@@ -1,9 +1,9 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
3
3
4−module Data.OpenApi.Compare.Validate.Param
4+module OpenAPI.Checker.Validate.Param
5− ( Behave (..),
5+ ( Behave (..)
6− Issue (..),
6+ , Issue (..)
7 )
7 )
8where
8where
9
9
@@ -11,11 +11,11 @@ import Control.Monad
11import Data.Functor
11import Data.Functor
12import Data.Maybe
12import Data.Maybe
13import Data.OpenApi
13import 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 ()
18import Data.Text as T
14import 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 ()
19import Text.Pandoc.Builder
19import Text.Pandoc.Builder
20
20
21-- | The type is normalized encoding style of the parameter. If two encoding
21-- | The type is normalized encoding style of the parameter. If two encoding
@@ -68,9 +68,7 @@ instance Issuable 'PathFragmentLevel where
68 ParamSchemaMismatch
68 ParamSchemaMismatch
69 | PathFragmentsDontMatch (ProdCons Text)
69 | PathFragmentsDontMatch (ProdCons Text)
70 deriving stock (Eq, Ord, Show)
70 deriving stock (Eq, Ord, Show)
71− issueKind = \case
71+ issueIsUnsupported _ = False
72− ParamSchemaMismatch -> ProbablyIssue -- the schema could be catch-all (?)
73− _ -> CertainIssue
74 describeIssue _ ParamNameMismatch = para "The path fragments don't match."
72 describeIssue _ ParamNameMismatch = para "The path fragments don't match."
75 describeIssue Forward ParamEmptinessIncompatible = para "The parameter can no longer be empty."
73 describeIssue Forward ParamEmptinessIncompatible = para "The parameter can no longer be empty."
76 describeIssue Backward ParamEmptinessIncompatible = para "The parameter can now be empty."
74 describeIssue Backward ParamEmptinessIncompatible = para "The parameter can now be empty."
@@ -79,7 +77,7 @@ instance Issuable 'PathFragmentLevel where
79 describeIssue _ ParamPlaceIncompatible = para "Parameters in incompatible locations."
77 describeIssue _ ParamPlaceIncompatible = para "Parameters in incompatible locations."
80 describeIssue _ ParamStyleMismatch = para "Different parameter styles (encodings)."
78 describeIssue _ ParamStyleMismatch = para "Different parameter styles (encodings)."
81 describeIssue _ ParamSchemaMismatch = para "Expected a schema, but didn't find one."
79 describeIssue _ ParamSchemaMismatch = para "Expected a schema, but didn't find one."
82− describeIssue ori (PathFragmentsDontMatch (orientProdCons ori -> ProdCons e a)) =
80+ describeIssue ori (PathFragmentsDontMatch (orientProdCons ori -> ProdCons e a)) =
83 para $ "Parameter changed from " <> code e <> " to " <> code a <> "."
81 para $ "Parameter changed from " <> code e <> " to " <> code a <> "."
84
82
85instance Behavable 'PathFragmentLevel 'SchemaLevel where
83instance Behavable 'PathFragmentLevel 'SchemaLevel where
@@ -87,7 +85,7 @@ instance Behavable 'PathFragmentLevel 'SchemaLevel where
87 = InParamSchema
85 = InParamSchema
88 deriving stock (Eq, Ord, Show)
86 deriving stock (Eq, Ord, Show)
89
87
90− describeBehavior InParamSchema = "JSON Schema"
88+ describeBehaviour InParamSchema = "JSON Schema"
91
89
92instance Subtree Param where
90instance Subtree Param where
93 type SubtreeLevel Param = 'PathFragmentLevel
91 type SubtreeLevel Param = 'PathFragmentLevel
@@ -106,17 +104,15 @@ instance Subtree Param where
106 when (_paramName (extract p) /= _paramName (extract c)) $
104 when (_paramName (extract p) /= _paramName (extract c)) $
107 issueAt beh ParamNameMismatch
105 issueAt beh ParamNameMismatch
108 when
106 when
109− ( (fromMaybe False . _paramRequired . extract $ c)
107+ ((fromMaybe False . _paramRequired . extract $ c)
110− && not (fromMaybe False . _paramRequired . extract $ p)
108+ && not (fromMaybe False . _paramRequired . extract $ p))
111− )
112 $ issueAt beh ParamRequired
109 $ issueAt beh ParamRequired
113 case (_paramIn . extract $ p, _paramIn . extract $ c) of
110 case (_paramIn . extract $ p, _paramIn . extract $ c) of
114 (ParamQuery, ParamQuery) -> do
111 (ParamQuery, ParamQuery) -> do
115 -- Emptiness is only for query params
112 -- Emptiness is only for query params
116 when
113 when
117− ( (fromMaybe False . _paramAllowEmptyValue . extract $ p)
114+ ((fromMaybe False . _paramAllowEmptyValue . extract $ p)
118− && not (fromMaybe False . _paramAllowEmptyValue . extract $ c)
115+ && not (fromMaybe False . _paramAllowEmptyValue . extract $ c))
119− )
120 $ issueAt beh ParamEmptinessIncompatible
116 $ issueAt beh ParamEmptinessIncompatible
121 (a, b) | a == b -> pure ()
117 (a, b) | a == b -> pure ()
122 _ -> issueAt beh ParamPlaceIncompatible
118 _ -> issueAt beh ParamPlaceIncompatible
src/Data/OpenApi/Compare/Validate/PathFragment.hs → src/OpenAPI/Checker/Validate/PathFragment.hsrenamed+9−8
@@ -1,20 +1,21 @@
1−module Data.OpenApi.Compare.Validate.PathFragment
1+module OpenAPI.Checker.Validate.PathFragment
2− ( parsePath,
2+ ( parsePath
3− PathFragment (..),
3+ , PathFragment (..)
4− PathFragmentParam,
4+ , PathFragmentParam
5 )
5 )
6where
6where
7
7
8import qualified Data.Aeson as A
8import qualified Data.Aeson as A
9import Data.OpenApi
9import Data.OpenApi
10−import Data.OpenApi.Compare.Behavior
11−import Data.OpenApi.Compare.Subtree
12−import Data.OpenApi.Compare.Validate.Param
13import Data.Text (Text)
10import Data.Text (Text)
14import qualified Data.Text as T
11import 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
15
16
16-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
17-- 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
18parsePath :: FilePath -> [PathFragment Text]
19parsePath :: FilePath -> [PathFragment Text]
19parsePath = fmap partition . T.splitOn "/" . T.pack
20parsePath = fmap partition . T.splitOn "/" . T.pack
20 where
21 where
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 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.RequestBody
3+module OpenAPI.Checker.Validate.RequestBody
4− ( Issue (..),
4+ ( Issue (..)
5− Behave (..),
5+ , Behave (..)
6 )
6 )
7where
7where
8
8
@@ -11,12 +11,12 @@ import Data.HashMap.Strict.InsOrd as IOHM
11import Data.Map.Strict as M
11import Data.Map.Strict as M
12import Data.Maybe
12import Data.Maybe
13import Data.OpenApi
13import 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
18import qualified Data.Text as T
14import qualified Data.Text as T
19import Network.HTTP.Media (MediaType)
15import 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
20import Text.Pandoc.Builder
20import Text.Pandoc.Builder
21
21
22-- TODO: Use RequestMediaTypeObjectMapping
22-- TODO: Use RequestMediaTypeObjectMapping
@@ -30,8 +30,7 @@ instance Issuable 'RequestLevel where
30 = RequestBodyRequired
30 = RequestBodyRequired
31 | RequestMediaTypeNotFound MediaType
31 | RequestMediaTypeNotFound MediaType
32 deriving stock (Eq, Ord, Show)
32 deriving stock (Eq, Ord, Show)
33− issueKind = \case
33+ issueIsUnsupported _ = False
34− _ -> CertainIssue
35 describeIssue Forward RequestBodyRequired =
34 describeIssue Forward RequestBodyRequired =
36 para "Request body has become required."
35 para "Request body has become required."
37 describeIssue Backward RequestBodyRequired =
36 describeIssue Backward RequestBodyRequired =
@@ -45,7 +44,7 @@ instance Behavable 'RequestLevel 'PayloadLevel where
45 data Behave 'RequestLevel 'PayloadLevel
44 data Behave 'RequestLevel 'PayloadLevel
46 = InPayload
45 = InPayload
47 deriving stock (Eq, Ord, Show)
46 deriving stock (Eq, Ord, Show)
48− describeBehavior InPayload = "Payload"
47+ describeBehaviour InPayload = "Payload"
49
48
50instance Subtree RequestBody where
49instance Subtree RequestBody where
51 type SubtreeLevel RequestBody = 'RequestLevel
50 type SubtreeLevel RequestBody = 'RequestLevel
src/Data/OpenApi/Compare/Validate/Responses.hs → src/OpenAPI/Checker/Validate/Responses.hsrenamed+14−15
@@ -1,8 +1,8 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
3
3
4−module Data.OpenApi.Compare.Validate.Responses
4+module OpenAPI.Checker.Validate.Responses
5− ( Behave (..),
5+ ( Behave (..)
6 )
6 )
7where
7where
8
8
@@ -11,17 +11,17 @@ import Data.HashMap.Strict.InsOrd as IOHM
11import Data.Map.Strict as M
11import Data.Map.Strict as M
12import Data.Maybe
12import Data.Maybe
13import Data.OpenApi
13import 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
23import qualified Data.Text as T
14import qualified Data.Text as T
24import Network.HTTP.Media (MediaType)
15import 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
25import Text.Pandoc.Builder
25import Text.Pandoc.Builder
26
26
27tracedResponses :: Traced Responses -> IOHM.InsOrdHashMap HttpStatusCode (Traced (Referenced Response))
27tracedResponses :: Traced Responses -> IOHM.InsOrdHashMap HttpStatusCode (Traced (Referenced Response))
@@ -71,8 +71,7 @@ instance Issuable 'ResponseLevel where
71 = ConsumerDoesntHaveMediaType MediaType
71 = ConsumerDoesntHaveMediaType MediaType
72 | ProducerDoesntHaveResponseHeader HeaderName
72 | ProducerDoesntHaveResponseHeader HeaderName
73 deriving stock (Eq, Ord, Show)
73 deriving stock (Eq, Ord, Show)
74− issueKind = \case
74+ issueIsUnsupported _ = False
75− _ -> CertainIssue
76 describeIssue Forward (ConsumerDoesntHaveMediaType t) =
75 describeIssue Forward (ConsumerDoesntHaveMediaType t) =
77 para $ "Media type was removed: " <> (code . T.pack . show $ t) <> "."
76 para $ "Media type was removed: " <> (code . T.pack . show $ t) <> "."
78 describeIssue Backward (ConsumerDoesntHaveMediaType t) =
77 describeIssue Backward (ConsumerDoesntHaveMediaType t) =
@@ -87,14 +86,14 @@ instance Behavable 'ResponseLevel 'PayloadLevel where
87 = ResponsePayload
86 = ResponsePayload
88 deriving stock (Eq, Ord, Show)
87 deriving stock (Eq, Ord, Show)
89
88
90− describeBehavior ResponsePayload = "Payload"
89+ describeBehaviour ResponsePayload = "Payload"
91
90
92instance Behavable 'ResponseLevel 'HeaderLevel where
91instance Behavable 'ResponseLevel 'HeaderLevel where
93 data Behave 'ResponseLevel 'HeaderLevel
92 data Behave 'ResponseLevel 'HeaderLevel
94 = InHeader HeaderName
93 = InHeader HeaderName
95 deriving stock (Eq, Ord, Show)
94 deriving stock (Eq, Ord, Show)
96
95
97− describeBehavior (InHeader name) = "Header " <> code name
96+ describeBehaviour (InHeader name) = "Header " <> code name
98
97
99instance Subtree Response where
98instance Subtree Response where
100 type SubtreeLevel Response = 'ResponseLevel
99 type SubtreeLevel Response = 'ResponseLevel
src/Data/OpenApi/Compare/Validate/Schema.hs → src/OpenAPI/Checker/Validate/Schema.hsrenamed+116−96
@@ -1,74 +1,72 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2−
3−module Data.OpenApi.Compare.Validate.Schema
2+module OpenAPI.Checker.Validate.Schema
4 (
3 (
5 )
4 )
6where
5where
7
6
8−import Control.Monad (when,unless)
7+import Control.Monad.Writer
9import qualified Data.Aeson as A
8import qualified Data.Aeson as A
10import Data.Coerce
9import Data.Coerce
11import Data.Foldable (for_, toList)
10import Data.Foldable (for_, toList)
12import Data.Functor
11import Data.Functor
13import Data.HList
12import Data.HList
14−import Data.List (genericIndex, genericLength, group)
15import qualified Data.List.NonEmpty as NE
13import qualified Data.List.NonEmpty as NE
16import qualified Data.Map as M
14import qualified Data.Map as M
15+import Data.List (group, genericLength, genericIndex)
17import Data.Maybe
16import Data.Maybe
18import Data.OpenApi
17import 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
30import Data.Ord
18import Data.Ord
31import Data.Ratio
19import Data.Ratio
32import Data.Semigroup
20import Data.Semigroup
33import qualified Data.Set as S
21import qualified Data.Set as S
34import Data.Text (Text)
22import 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
35
34
36−checkFormulas ::
35+checkFormulas
37− (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
36+ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
38− HList xs ->
37+ => HList xs
39− Behavior 'SchemaLevel ->
38+ -> Behavior 'SchemaLevel
40− ProdCons (Trace Schema) ->
39+ -> ProdCons (Trace Schema)
41− ProdCons (Traced (Definitions Schema)) ->
40+ -> ProdCons (Traced (Definitions Schema))
42− ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel) ->
41+ -> ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
43− SemanticCompatFormula ()
42+ -> SemanticCompatFormula ()
44checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
43checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
45 case P.toList ep ++ P.toList ec of
44 case P.toList ep ++ P.toList ec of
46 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
45 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
47 [] -> do
46 [] -> do
48 -- We have the following isomorphisms:
47 -- We have the following isomorphisms:
48+ -- (A ⊂ X ∪ Y) = (A ⊂ X) \/ (A ⊂ Y)
49 -- (A ⊂ X ∩ Y) = (A ⊂ X) /\ (A ⊂ Y)
49 -- (A ⊂ X ∩ Y) = (A ⊂ X) /\ (A ⊂ Y)
50 -- (A ⊂ ⊤) = 1
50 -- (A ⊂ ⊤) = 1
51 -- (X ∪ Y ⊂ B) = (X ⊂ B) /\ (Y ⊂ B)
51 -- (X ∪ Y ⊂ B) = (X ⊂ B) /\ (Y ⊂ B)
52 -- (∅ ⊂ B) = 1
52 -- (∅ ⊂ B) = 1
53 -- The remaining cases are, notably, not isomorphisms:
53 -- The remaining cases are, notably, not isomorphisms:
54− -- 1) (A ⊂ X ∪ Y) <= (A ⊂ X) \/ (A ⊂ Y)
54+ -- 1) (A ⊂ ∅) <= 0
55− -- 2) (A ⊂ ∅) <= 0
56− -- 3) (X ∩ Y ⊂ B) <= (X ⊂ B) \/ (Y ⊂ B)
55+ -- 2) (X ∩ Y ⊂ B) <= (X ⊂ B) \/ (Y ⊂ B)
57− -- 4) (⊤ ⊂ B) <= 0
56+ -- 3) (⊤ ⊂ B) <= 0
58− -- Therefore we have the implications with (∃ and ∀ being the N-ary
57+ -- Therefore we have the isomorphisms with (∃ and ∀ being the N-ary
59 -- versions of \/ and /\ respectively):
58 -- versions of \/ and /\ respectively):
60 -- (⋃_i ⋂_j A[i,j]) ⊂ (⋃_k ⋂_l B[k,l])
59 -- (⋃_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]
62 -- = ∀i ∃k ∀l, (⋂_j A[i,j]) ⊂ B[k,l]
61 -- = ∀i ∃k ∀l, (⋂_j A[i,j]) ⊂ B[k,l]
63 -- with the caveat that the the set over which k ranges is nonempty.
62 -- 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.
66 -- Our disjunction loses information, so it makes sense to nest it as
64 -- Our disjunction loses information, so it makes sense to nest it as
67 -- deeply as possible, hence we choose the latter representation.
65 -- deeply as possible, hence we choose the latter representation.
68 --
66 --
69 -- We delegate the verification of (⋂_j A[j]) ⊂ B to a separate heuristic
67 -- We delegate the verification of (⋂_j A[j]) ⊂ B to a separate heuristic
70 -- function, with the understanding that ∃j, A[j] ⊂ B is a sufficient,
68 -- 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)).
72 --
70 --
73 -- If k ranges over an empty set, we have the isomorphism:
71 -- If k ranges over an empty set, we have the isomorphism:
74 -- (⋃_i ⋂_j A[i,j]) ⊂ ∅ = ∀i, (⋂_j A[i,j]) ⊂ ∅
72 -- (⋃_i ⋂_j A[i,j]) ⊂ ∅ = ∀i, (⋂_j A[i,j]) ⊂ ∅
@@ -145,21 +143,21 @@ checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
145 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
143 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
146 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
144 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
147
145
148−checkContradiction ::
146+checkContradiction
149− Behavior 'TypedSchemaLevel ->
147+ :: Behavior 'TypedSchemaLevel
150− Maybe Partition ->
148+ -> Maybe Partition
151− S.Set (Condition t) ->
149+ -> S.Set (Condition t)
152− SemanticCompatFormula ()
150+ -> SemanticCompatFormula ()
153checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
151checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
154
152
155−checkImplication ::
153+checkImplication
156− (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
154+ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
157− HList xs ->
155+ => HList xs
158− Behavior 'TypedSchemaLevel ->
156+ -> Behavior 'TypedSchemaLevel
159− ProdCons (Trace Schema) -> -- the traces of the root schemas used in this comparison
157+ -> ProdCons (Trace Schema) -- the traces of the root schemas used in this comparison
160− S.Set (Condition t) ->
158+ -> S.Set (Condition t)
161− Condition t ->
159+ -> Condition t
162− SemanticCompatFormula ()
160+ -> SemanticCompatFormula ()
163checkImplication env beh trs prods cons = case findExactly prods of
161checkImplication env beh trs prods cons = case findExactly prods of
164 Just e
162 Just e
165 | all (satisfiesTyped e) prods ->
163 | all (satisfiesTyped e) prods ->
@@ -167,104 +165,124 @@ checkImplication env beh trs prods cons = case findExactly prods of
167 then pure ()
165 then pure ()
168 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
166 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
169 | otherwise -> pure () -- vacuously true
167 | otherwise -> pure () -- vacuously true
168+
170 Nothing -> case cons of
169 Nothing -> case cons of
171 -- the above code didn't catch it, so there's no Exactly condition on the lhs
170 -- the above code didn't catch it, so there's no Exactly condition on the lhs
172 Exactly e -> issueAt beh (NoMatchingEnum $ untypeValue e)
171 Exactly e -> issueAt beh (NoMatchingEnum $ untypeValue e)
172+
173 Maximum m -> foldCheck min m NoMatchingMaximum MatchingMaximumWeak $ \case
173 Maximum m -> foldCheck min m NoMatchingMaximum MatchingMaximumWeak $ \case
174 Maximum m' -> Just m'
174 Maximum m' -> Just m'
175 _ -> Nothing
175 _ -> Nothing
176+
176 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
177 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
177 Minimum m' -> Just m'
178 Minimum m' -> Just m'
178 _ -> Nothing
179 _ -> Nothing
180+
179 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
181 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
180 MultipleOf m' -> Just m'
182 MultipleOf m' -> Just m'
181 _ -> Nothing
183 _ -> Nothing
184+
182 NumberFormat f -> case flip any prods $ \case
185 NumberFormat f -> case flip any prods $ \case
183− NumberFormat f' -> f == f'
186+ NumberFormat f' -> f == f'
184− _ -> False of
187+ _ -> False
188+ of
185 True -> pure ()
189 True -> pure ()
186 False -> issueAt beh (NoMatchingFormat f)
190 False -> issueAt beh (NoMatchingFormat f)
191+
187 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
192 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
188 MaxLength m' -> Just m'
193 MaxLength m' -> Just m'
189 _ -> Nothing
194 _ -> Nothing
195+
190 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
196 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
191 MinLength m' -> Just m'
197 MinLength m' -> Just m'
192 _ -> Nothing
198 _ -> Nothing
199+
193 Pattern p -> case flip any prods $ \case
200 Pattern p -> case flip any prods $ \case
194− Pattern p' -> p == p'
201+ Pattern p' -> p == p'
195− _ -> False of
202+ _ -> False
203+ of
196 True -> pure ()
204 True -> pure ()
197 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
205 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
206+
198 StringFormat f -> case flip any prods $ \case
207 StringFormat f -> case flip any prods $ \case
199− StringFormat f' -> f == f'
208+ StringFormat f' -> f == f'
200− _ -> False of
209+ _ -> False
210+ of
201 True -> pure ()
211 True -> pure ()
202 False -> issueAt beh (NoMatchingFormat f)
212 False -> issueAt beh (NoMatchingFormat f)
213+
203 Items _ cons' -> case foldSome (<>) prods $ \case
214 Items _ cons' -> case foldSome (<>) prods $ \case
204− Items _ rs -> Just (Just (rs NE.:| []), mempty)
215+ Items _ rs -> Just (Just (rs NE.:| []), mempty)
205− TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
216+ TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
206− _ -> Nothing of
217+ _ -> Nothing
218+ of
207 Just (mItems, Just pfs)
219 Just (mItems, Just pfs)
208 | not $ allSame (length <$> pfs) -> pure () -- vacuously
220 | not $ allSame (length <$> pfs) -> pure () -- vacuously
209− | let plen = genericLength (NE.head pfs) ->
221+ | let plen = genericLength (NE.head pfs)
210− clarifyIssue (AnItem beh (anIssue TupleToArray)) $
222+ -> clarifyIssue (AnItem beh (anIssue TupleToArray)) $ for_ [0 .. plen - 1] $ \i -> do
211− for_ [0 .. plen - 1] $ \i -> do
212− let prod' = tracedConjunct $ case mItems of
223+ let prod' = tracedConjunct $ case mItems of
213− Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
224+ Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
214− Nothing -> (`genericIndex` i) <$> pfs
225+ Nothing -> (`genericIndex` i) <$> pfs
215− checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
226+ checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
216 Just (Just prods', Nothing) -> do
227 Just (Just prods', Nothing) -> do
217 let prod' = tracedConjunct prods'
228 let prod' = tracedConjunct prods'
218 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
229 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
219 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
230 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
220 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
231 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
232+
221 TupleItems (map snd -> fs) -> case foldSome (<>) prods $ \case
233 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)
234+ 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)
235+ MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
224− MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
236+ MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
225− Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
237+ Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
226− _ -> Nothing of
238+ _ -> Nothing
239+ of
227− -- if the length constraints in the producer are contradictory:
240+ -- if the length constraints in the producer are contradictory:
228− Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
241+ Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
229− -- We have an explicit tuple items clause...
242+ -- We have an explicit tuple items clause...
230− Just (Just pfs, Just (Max plen), _, _)
243+ Just (Just pfs, Just (Max plen), _, _)
231− | plen /= genericLength fs -> -- ...of wrong length
244+ | plen /= genericLength fs -- ...of wrong length
232− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
245+ -> issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
233− | otherwise ->
246+ | otherwise
234− for_ [0 .. plen - 1] $ \i -> do
247+ -> for_ [0 .. plen - 1] $ \i -> do
235 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
248 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
236− -- We have a fixed length array in the producer...
249+ -- We have a fixed length array in the producer...
237− Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
250+ Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
238− | plen == plen' ->
251+ | plen == plen'
239− clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
252+ -> clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
240− _
241− | plen /= genericLength fs -> -- ...of wrong length
253+ _ | plen /= genericLength fs -> -- ...of wrong length
242− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
254+ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
243 Just rs -> for_ [0 .. plen - 1] $ \i -> do
255 Just rs -> for_ [0 .. plen - 1] $ \i -> do
244 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
256 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
245 -- ...and no "items" schema
257 -- ...and no "items" schema
246 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
258 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
247 for_ [0 .. plen - 1] $ \i -> do
259 for_ [0 .. plen - 1] $ \i -> do
248 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
260 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
249− _ -> issueAt beh NoMatchingTupleItems
261+ _ -> issueAt beh NoMatchingTupleItems
262+
250 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
263 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
251 MaxItems m' -> Just m'
264 MaxItems m' -> Just m'
252 TupleItems fs -> Just $ toInteger $ length fs
265 TupleItems fs -> Just $ toInteger $ length fs
253 _ -> Nothing
266 _ -> Nothing
267+
254 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
268 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
255 MinItems m' -> Just m'
269 MinItems m' -> Just m'
256 TupleItems fs -> Just $ toInteger $ length fs
270 TupleItems fs -> Just $ toInteger $ length fs
257 _ -> Nothing
271 _ -> Nothing
272+
258 UniqueItems -> case flip any prods $ \case
273 UniqueItems -> case flip any prods $ \case
259− UniqueItems -> True
274+ UniqueItems -> True
260− MaxItems 1 -> True
275+ MaxItems 1 -> True
261− TupleItems fs | length fs == 1 -> True
276+ TupleItems fs | length fs == 1 -> True
262− _ -> False of
277+ _ -> False
278+ of
263 True -> pure ()
279 True -> pure ()
264 False -> issueAt beh NoMatchingUniqueItems
280 False -> issueAt beh NoMatchingUniqueItems
281+
265 Properties props _ madd -> case foldSome (<>) prods $ \case
282 Properties props _ madd -> case foldSome (<>) prods $ \case
266− Properties props' _ madd' -> Just $ (props', madd') NE.:| []
283+ Properties props' _ madd' -> Just $ (props', madd') NE.:| []
267− _ -> Nothing of
284+ _ -> Nothing
285+ of
268 Just pm ->
286 Just pm ->
269 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
287 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
270 NE.toList pm <&> \(props', madd') -> do
288 NE.toList pm <&> \(props', madd') -> do
@@ -295,9 +313,11 @@ checkImplication env beh trs prods cons = case findExactly prods of
295 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
313 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
296 pure ()
314 pure ()
297 Nothing -> issueAt beh NoMatchingProperties
315 Nothing -> issueAt beh NoMatchingProperties
316+
298 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
317 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
299 MaxProperties m' -> Just m'
318 MaxProperties m' -> Just m'
300 _ -> Nothing
319 _ -> Nothing
320+
301 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
321 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
302 MinProperties m' -> Just m'
322 MinProperties m' -> Just m'
303 _ -> Nothing
323 _ -> Nothing
@@ -305,14 +325,14 @@ checkImplication env beh trs prods cons = case findExactly prods of
305 lcmScientific (toRational -> a) (toRational -> b) =
325 lcmScientific (toRational -> a) (toRational -> b) =
306 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
326 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
307
327
308− foldCheck ::
328+ foldCheck
309− Eq a =>
329+ :: Eq a
310− (a -> a -> a) ->
330+ => (a -> a -> a)
311− a ->
331+ -> a
312− (a -> Issue 'TypedSchemaLevel) ->
332+ -> (a -> Issue 'TypedSchemaLevel)
313− (ProdCons a -> Issue 'TypedSchemaLevel) ->
333+ -> (ProdCons a -> Issue 'TypedSchemaLevel)
314− (forall t. Condition t -> Maybe a) ->
334+ -> (forall t. Condition t -> Maybe a)
315− SemanticCompatFormula ()
335+ -> SemanticCompatFormula ()
316 foldCheck f m missing weak extr = case foldSome f prods extr of
336 foldCheck f m missing weak extr = case foldSome f prods extr of
317 Just m'
337 Just m'
318 | f m' m == m' -> pure ()
338 | 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
1+module OpenAPI.Checker.Validate.Schema.DNF
2− ( DNF (..),
2+ ( DNF (..)
3− Disjunct (..),
3+ , Disjunct (..)
4− pattern SingleDisjunct,
4+ , pattern SingleDisjunct
5− pattern TopDNF,
5+ , pattern TopDNF
6− pattern BottomDNF,
6+ , pattern BottomDNF
7− pattern LiteralDNF,
7+ , pattern LiteralDNF
8− foldDNF,
8+ , foldDNF
9− forDNF,
9+ , forDNF
10 )
10 )
11where
11where
12
12
src/Data/OpenApi/Compare/Validate/Schema/Issues.hs → src/OpenAPI/Checker/Validate/Schema/Issues.hsrenamed+24−95
@@ -1,21 +1,21 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.Schema.Issues
3+module OpenAPI.Checker.Validate.Schema.Issues
4− ( Issue (..),
4+ ( Issue (..)
5− Behave (..),
5+ , Behave (..)
6 )
6 )
7where
7where
8
8
9import qualified Data.Aeson as A
9import qualified Data.Aeson as A
10import Data.OpenApi
10import 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
16import Data.Scientific
11import Data.Scientific
17import Data.Text (Text)
12import Data.Text (Text)
18import qualified Data.Text as T
13import 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
19import Text.Pandoc.Builder hiding (Format, Null)
19import Text.Pandoc.Builder hiding (Format, Null)
20
20
21instance Issuable 'TypedSchemaLevel where
21instance Issuable 'TypedSchemaLevel where
@@ -87,67 +87,7 @@ instance Issuable 'TypedSchemaLevel where
87 | -- | consumer indicates that no values in a particular partition are allowed, but we weren't able to conclude this in the producer
87 | -- | consumer indicates that no values in a particular partition are allowed, but we weren't able to conclude this in the producer
88 PartitionBecomesEmpty Partition
88 PartitionBecomesEmpty Partition
89 deriving stock (Eq, Ord, Show)
89 deriving stock (Eq, Ord, Show)
90− issueKind = \case
90+ issueIsUnsupported _ = False
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
151 describeIssue Forward (EnumDoesntSatisfy v) = para "The following enum value was removed:" <> showJSONValue v
91 describeIssue Forward (EnumDoesntSatisfy v) = para "The following enum value was removed:" <> showJSONValue v
152 describeIssue Backward (EnumDoesntSatisfy v) = para "The following enum value was added:" <> showJSONValue v
92 describeIssue Backward (EnumDoesntSatisfy v) = para "The following enum value was added:" <> showJSONValue v
153 describeIssue Forward (NoMatchingEnum v) = para "The following enum value has been added:" <> showJSONValue v
93 describeIssue Forward (NoMatchingEnum v) = para "The following enum value has been added:" <> showJSONValue v
@@ -246,26 +186,15 @@ instance Issuable 'SchemaLevel where
246 | -- | producer allows a property that is not allowed in the consumer
186 | -- | producer allows a property that is not allowed in the consumer
247 UnexpectedProperty
187 UnexpectedProperty
248 deriving stock (Eq, Ord, Show)
188 deriving stock (Eq, Ord, Show)
249− issueKind = \case
189+ issueIsUnsupported = \case
250− NotSupported _ -> Unsupported
190+ NotSupported _ -> True
251− OneOfNotDisjoint -> Unsupported
191+ OneOfNotDisjoint -> True
252− InvalidSchema _ -> SchemaInvalid
192+ InvalidSchema _ -> True
253− UnguardedRecursion -> Unsupported
193+ UnguardedRecursion -> True
254− AdditionalToProperty -> Comment
194+ _ -> False
255− PropertyToAdditional -> Comment
195+
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
267 describeIssue _ (NotSupported i) =
196 describeIssue _ (NotSupported i) =
268− para (emph "Encountered a feature that CompaREST does not support: " <> text i <> ".")
197+ para (emph "Encountered a feature that OpenApi Diff does not support: " <> text i <> ".")
269 describeIssue _ OneOfNotDisjoint =
198 describeIssue _ OneOfNotDisjoint =
270 para $
199 para $
271 "Could not deduce that " <> code "oneOf"
200 "Could not deduce that " <> code "oneOf"
@@ -277,7 +206,7 @@ instance Issuable 'SchemaLevel where
277 describeIssue _ (InvalidSchema i) =
206 describeIssue _ (InvalidSchema i) =
278 para (emph "The schema is invalid: " <> text i <> ".")
207 para (emph "The schema is invalid: " <> text i <> ".")
279 describeIssue _ UnguardedRecursion =
208 describeIssue _ UnguardedRecursion =
280− para "Encountered recursion that is too complex for CompaREST to untangle."
209+ para "Encountered recursion that is too complex for OpenApi Diff to untangle."
281 describeIssue Forward (TypesRestricted tys) = case tys of
210 describeIssue Forward (TypesRestricted tys) = case tys of
282 [] -> para "No longer has any valid values." -- weird
211 [] -> para "No longer has any valid values." -- weird
283 _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
212 _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
@@ -298,14 +227,14 @@ instance Behavable 'SchemaLevel 'TypedSchemaLevel where
298 = OfType JsonType
227 = OfType JsonType
299 deriving stock (Eq, Ord, Show)
228 deriving stock (Eq, Ord, Show)
300
229
301− describeBehavior (OfType t) = describeJSONType t
230+ describeBehaviour (OfType t) = describeJSONType t
302
231
303instance Behavable 'TypedSchemaLevel 'TypedSchemaLevel where
232instance Behavable 'TypedSchemaLevel 'TypedSchemaLevel where
304 data Behave 'TypedSchemaLevel 'TypedSchemaLevel
233 data Behave 'TypedSchemaLevel 'TypedSchemaLevel
305 = InPartition Partition
234 = InPartition Partition
306 deriving stock (Eq, Ord, Show)
235 deriving stock (Eq, Ord, Show)
307
236
308− describeBehavior (InPartition partition) = "In cases where " <> showPartition partition
237+ describeBehaviour (InPartition partition) = "In cases where " <> showPartition partition
309
238
310instance Behavable 'TypedSchemaLevel 'SchemaLevel where
239instance Behavable 'TypedSchemaLevel 'SchemaLevel where
311 data Behave 'TypedSchemaLevel 'SchemaLevel
240 data Behave 'TypedSchemaLevel 'SchemaLevel
@@ -315,7 +244,7 @@ instance Behavable 'TypedSchemaLevel 'SchemaLevel where
315 | InAdditionalProperty
244 | InAdditionalProperty
316 deriving stock (Eq, Ord, Show)
245 deriving stock (Eq, Ord, Show)
317
246
318− describeBehavior InItems = "Items"
247+ describeBehaviour InItems = "Items"
319− describeBehavior (InItem i) = "Item " <> show' i
248+ describeBehaviour (InItem i) = "Item " <> show' i
320− describeBehavior (InProperty p) = "Property " <> code p
249+ describeBehaviour (InProperty p) = "Property " <> code p
321− describeBehavior InAdditionalProperty = "Additional properties"
250+ 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
1+module OpenAPI.Checker.Validate.Schema.JsonFormula
2− ( Bound (..),
2+ ( Bound (..)
3− showBound,
3+ , showBound
4− Property (..),
4+ , Property (..)
5− Condition (..),
5+ , Condition (..)
6− showCondition,
6+ , showCondition
7− satisfiesTyped,
7+ , satisfiesTyped
8− checkStringFormat,
8+ , checkStringFormat
9− checkNumberFormat,
9+ , checkNumberFormat
10− SomeCondition (..),
10+ , SomeCondition (..)
11− JsonFormula (..),
11+ , JsonFormula (..)
12− satisfiesFormula,
12+ , satisfiesFormula
13− satisfies,
13+ , satisfies
14− showJSONValue,
14+ , showJSONValue
15− showJSONValueInline,
15+ , showJSONValueInline
16 )
16 )
17where
17where
18
18
19import Algebra.Lattice
19import Algebra.Lattice
20import qualified Data.Aeson as A
20import qualified Data.Aeson as A
21−import qualified Data.Aeson.Key as Key
22−import qualified Data.Aeson.KeyMap as KeyMap
23import qualified Data.ByteString.Lazy as BSL
21import qualified Data.ByteString.Lazy as BSL
24import qualified Data.Foldable as F
22import qualified Data.Foldable as F
23+import qualified Data.HashMap.Strict as HM
25import Data.Functor
24import Data.Functor
26import Data.Int
25import Data.Int
27import Data.Kind
26import Data.Kind
28import qualified Data.Map as M
27import qualified Data.Map as M
29import Data.Maybe
28import Data.Maybe
30import Data.OpenApi
29import 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
35import Data.Ord
30import Data.Ord
36import Data.Ratio
31import Data.Ratio
37import Data.Scientific
32import Data.Scientific
@@ -40,7 +35,11 @@ import Data.Text (Text)
40import qualified Data.Text as T
35import qualified Data.Text as T
41import qualified Data.Text.Encoding as T
36import qualified Data.Text.Encoding as T
42import Data.Typeable
37import Data.Typeable
38+import OpenAPI.Checker.Orphans ()
39+import OpenAPI.Checker.Subtree
40+import OpenAPI.Checker.Validate.Schema.DNF
41+import OpenAPI.Checker.Validate.Schema.TypedJson
43−import Text.Pandoc.Builder hiding (Format)
42+import Text.Pandoc.Builder hiding (Format, Null)
44import Text.Regex.Pcre2
43import Text.Regex.Pcre2
45
44
46data Bound a = Exclusive !a | Inclusive !a
45data Bound a = Exclusive !a | Inclusive !a
@@ -68,33 +67,30 @@ data Property = Property
68data Condition :: JsonType -> Type where
67data Condition :: JsonType -> Type where
69 Exactly :: TypedValue t -> Condition t
68 Exactly :: TypedValue t -> Condition t
70 Maximum :: !(Bound Scientific) -> Condition 'Number
69 Maximum :: !(Bound Scientific) -> Condition 'Number
71− Minimum ::
70+ Minimum
72− !(Down (Bound (Down Scientific))) ->
71+ :: !(Down (Bound (Down Scientific)))
73− -- | this has the right Ord
72+ -> Condition 'Number -- ^ this has the right Ord
74− Condition 'Number
75 MultipleOf :: !Scientific -> Condition 'Number
73 MultipleOf :: !Scientific -> Condition 'Number
76 NumberFormat :: !Format -> Condition 'Number
74 NumberFormat :: !Format -> Condition 'Number
77 MaxLength :: !Integer -> Condition 'String
75 MaxLength :: !Integer -> Condition 'String
78 MinLength :: !Integer -> Condition 'String
76 MinLength :: !Integer -> Condition 'String
79 Pattern :: !Pattern -> Condition 'String
77 Pattern :: !Pattern -> Condition 'String
80 StringFormat :: !Format -> Condition 'String
78 StringFormat :: !Format -> Condition 'String
81− Items ::
79+ Items
82− !(ForeachType JsonFormula) ->
80+ :: !(ForeachType JsonFormula)
83− !(Traced (Referenced Schema)) ->
81+ -> !(Traced (Referenced Schema))
84− Condition 'Array
82+ -> Condition 'Array
85− TupleItems ::
83+ TupleItems
86− ![(ForeachType JsonFormula, Traced (Referenced Schema))] ->
84+ :: ![(ForeachType JsonFormula, Traced (Referenced Schema))]
87− Condition 'Array
85+ -> Condition 'Array
88 MaxItems :: !Integer -> Condition 'Array
86 MaxItems :: !Integer -> Condition 'Array
89 MinItems :: !Integer -> Condition 'Array
87 MinItems :: !Integer -> Condition 'Array
90 UniqueItems :: Condition 'Array
88 UniqueItems :: Condition 'Array
91− Properties ::
89+ Properties
92− !(M.Map Text Property) ->
90+ :: !(M.Map Text Property)
93− -- | formula for additional properties
94− !(ForeachType JsonFormula) ->
91+ -> !(ForeachType JsonFormula) -- ^ formula for additional properties
95− -- | schema for additional properties, Nothing means bottom
92+ -> !(Maybe (Traced (Referenced Schema))) -- ^ schema for additional properties, Nothing means bottom
96− !(Maybe (Traced (Referenced Schema))) ->
97− Condition 'Object
93+ -> Condition 'Object
98 MaxProperties :: !Integer -> Condition 'Object
94 MaxProperties :: !Integer -> Condition 'Object
99 MinProperties :: !Integer -> Condition 'Object
95 MinProperties :: !Integer -> Condition 'Object
100
96
@@ -122,15 +118,13 @@ showCondition = \case
122 UniqueItems -> para "The elements in the array should be unique."
118 UniqueItems -> para "The elements in the array should be unique."
123 (Properties props additional _) ->
119 (Properties props additional _) ->
124 bulletList $
120 bulletList $
125− ( M.toList props
121+ (M.toList props
126− <&> ( \(k, p) ->
122+ <&> (\(k, p) ->
127− para (code k)
123+ para (code k)
128− <> para (strong $ if propRequired p then "Required" else "Optional")
124+ <> para (strong $ if propRequired p then "Required" else "Optional")
129− <> showForEachJsonFormula (propFormula p)
125+ <> showForEachJsonFormula (propFormula p)))
130− )
131− )
132 <> [ para (emph "Additional properties")
126 <> [ para (emph "Additional properties")
133− <> showForEachJsonFormula additional
127+ <> showForEachJsonFormula additional
134 ]
128 ]
135 (MaxProperties n) -> para $ "The maximum number of fields should be " <> show' n <> "."
129 (MaxProperties n) -> para $ "The maximum number of fields should be " <> show' n <> "."
136 (MinProperties n) -> para $ "The minimum number of fields should be " <> show' n <> "."
130 (MinProperties n) -> para $ "The minimum number of fields should be " <> show' n <> "."
@@ -139,17 +133,15 @@ showCondition = \case
139 showForEachJsonFormula i =
133 showForEachJsonFormula i =
140 bulletList $
134 bulletList $
141 foldType
135 foldType
142− ( \t f -> case getJsonFormula $ f i of
136+ (\t f -> case getJsonFormula $ f i of
143− BottomDNF -> mempty
137+ BottomDNF -> mempty
144− (DNF conds) ->
138+ (DNF conds) ->
145 [ para (describeJSONType t)
139 [ para (describeJSONType t)
146 <> bulletList
140 <> bulletList
147− ( S.toList conds <&> \case
141+ (S.toList conds <&> \case
148− Disjunct (S.toList -> []) -> para "Empty"
142+ Disjunct (S.toList -> []) -> para "Empty"
149− Disjunct (S.toList -> cond) -> bulletList (showCondition <$> cond)
143+ Disjunct (S.toList -> cond) -> bulletList (showCondition <$> cond))
150− )
151− ]
144+ ])
152− )
153
145
154showJSONValue :: A.Value -> Blocks
146showJSONValue :: A.Value -> Blocks
155showJSONValue v = codeBlockWith ("", ["json"], mempty) (T.decodeUtf8 . BSL.toStrict . A.encode $ v)
147showJSONValue 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
178satisfiesTyped (TArray a) (MinItems m) = fromIntegral (F.length a) >= m
170satisfiesTyped (TArray a) (MinItems m) = fromIntegral (F.length a) >= m
179satisfiesTyped (TArray a) UniqueItems = S.size (S.fromList $ F.toList a) == F.length a -- TODO: could be better #36
171satisfiesTyped (TArray a) UniqueItems = S.size (S.fromList $ F.toList a) == F.length a -- TODO: could be better #36
180satisfiesTyped (TObject o) (Properties props additional _) =
172satisfiesTyped (TObject o) (Properties props additional _) =
181− all (`KeyMap.member` o) (map Key.fromText $ M.keys (M.filter propRequired props))
173+ all (`HM.member` o) (M.keys (M.filter propRequired props))
182− && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup (Key.toText k) props) (KeyMap.toList o)
174+ && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup k props) (HM.toList o)
183−satisfiesTyped (TObject o) (MaxProperties m) = fromIntegral (KeyMap.size o) <= m
175+satisfiesTyped (TObject o) (MaxProperties m) = fromIntegral (HM.size o) <= m
184−satisfiesTyped (TObject o) (MinProperties m) = fromIntegral (KeyMap.size o) >= m
176+satisfiesTyped (TObject o) (MinProperties m) = fromIntegral (HM.size o) >= m
185
177
186checkNumberFormat :: Format -> Scientific -> Bool
178checkNumberFormat :: Format -> Scientific -> Bool
187checkNumberFormat "int32" (toRational -> n) =
179checkNumberFormat "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
1+module OpenAPI.Checker.Validate.Schema.Partition
2− ( partitionSchema,
2+ ( partitionSchema
3− partitionRefSchema,
3+ , partitionRefSchema
4− selectPartition,
4+ , selectPartition
5− runPartitionM,
5+ , runPartitionM
6− tryPartition,
6+ , tryPartition
7− showPartition,
7+ , showPartition
8− intersectSchema,
8+ , intersectSchema
9− intersectRefSchema,
9+ , intersectRefSchema
10− IntersectionResult (..),
10+ , IntersectionResult (..)
11− runIntersectionM,
11+ , runIntersectionM
12− Partition,
12+ , Partition
13 )
13 )
14where
14where
15
15
16import Algebra.Lattice
16import Algebra.Lattice
17import Algebra.Lattice.Lifted
17import Algebra.Lattice.Lifted
18import Control.Applicative
18import Control.Applicative
19−import Control.Monad (when)
20import Control.Monad.Reader hiding (ask)
19import Control.Monad.Reader hiding (ask)
21import qualified Control.Monad.Reader as R
20import qualified Control.Monad.Reader as R
22import Control.Monad.State
21import Control.Monad.State
@@ -32,21 +31,19 @@ import qualified Data.List as L
32import qualified Data.List.NonEmpty as NE
31import qualified Data.List.NonEmpty as NE
33import qualified Data.Map as M
32import qualified Data.Map as M
34import Data.Maybe
33import Data.Maybe
35−import Data.Monoid
36import Data.OpenApi
34import 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
44import Data.Ord
35import Data.Ord
45import qualified Data.Set as S
36import qualified Data.Set as S
46import Data.Text (Text)
37import Data.Text (Text)
47import qualified Data.Text as T
38import qualified Data.Text as T
48−import Data.Traversable (forM)
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
49−import Text.Pandoc.Builder hiding (Format)
46+import Text.Pandoc.Builder hiding (Format, Null)
50
47
51data PartitionData
48data PartitionData
52 = DByEnumValue (DNF (S.Set A.Value))
49 = DByEnumValue (DNF (S.Set A.Value))
@@ -146,11 +143,10 @@ partitionCondition = \case
146 Just _ -> top
143 Just _ -> top
147 Nothing ->
144 Nothing ->
148 singletonPart $
145 singletonPart $
149− DByProperties $
146+ DByProperties $ LiteralDNF
150− LiteralDNF
151− ( M.keysSet $ M.filter (not . propRequired) props
147+ ( M.keysSet $ M.filter (not . propRequired) props
152− , M.keysSet $ M.filter propRequired props
148+ , M.keysSet $ M.filter propRequired props
153− )
149+ )
154 inProps <- forM (M.toList $ M.filter propRequired props) $ \(k, prop) -> do
150 inProps <- forM (M.toList $ M.filter propRequired props) $ \(k, prop) -> do
155 f <- partitionRefSchema $ propRefSchema prop
151 f <- partitionRefSchema $ propRefSchema prop
156 pure $ fmap (\(Partitions m) -> Partitions $ M.mapKeysMonotonic (PInProperty k) m) f
152 pure $ fmap (\(Partitions m) -> Partitions $ M.mapKeysMonotonic (PInProperty k) m) f
@@ -160,10 +156,10 @@ partitionCondition = \case
160runPartitionM :: Traced (Definitions Schema) -> PartitionM a -> a
156runPartitionM :: Traced (Definitions Schema) -> PartitionM a -> a
161runPartitionM defs = runIdentity . runMemo () . (`runReaderT` defs)
157runPartitionM defs = runIdentity . runMemo () . (`runReaderT` defs)
162
158
163−partitionJsonFormulas ::
159+partitionJsonFormulas
164− ProdCons (Traced (Definitions Schema)) ->
160+ :: ProdCons (Traced (Definitions Schema))
165− ProdCons (JsonFormula t) ->
161+ -> ProdCons (JsonFormula t)
166− Lifted Partitions
162+ -> Lifted Partitions
167partitionJsonFormulas defs pc = producer pcPart \/ consumer pcPart
163partitionJsonFormulas defs pc = producer pcPart \/ consumer pcPart
168 where
164 where
169 pcPart = partitionFormula <$> defs <*> pc
165 pcPart = partitionFormula <$> defs <*> pc
@@ -215,11 +211,11 @@ runIntersectionM defs act = case runWriterT $ runReaderT act defs of
215 Just (x, Any False) -> Same x
211 Just (x, Any False) -> Same x
216 Just (x, Any True) -> New x
212 Just (x, Any True) -> New x
217
213
218−intersectSchema ::
214+intersectSchema
219− PartitionLocation ->
215+ :: PartitionLocation
220− PartitionChoice ->
216+ -> PartitionChoice
221− Traced Schema ->
217+ -> Traced Schema
222− IntersectionM Schema
218+ -> IntersectionM Schema
223intersectSchema loc part sch = do
219intersectSchema loc part sch = do
224 allOf' <- forM (tracedAllOf sch) $ \rss ->
220 allOf' <- forM (tracedAllOf sch) $ \rss ->
225 -- Assuming i ranges over a nonempty set (checked in processSchema)
221 -- Assuming i ranges over a nonempty set (checked in processSchema)
@@ -262,11 +258,11 @@ intersectSchema loc part sch = do
262 pure $ sch' {_schemaEnum = Just enum'}
258 pure $ sch' {_schemaEnum = Just enum'}
263 CByProperties {} -> error "CByProperties not implemented"
259 CByProperties {} -> error "CByProperties not implemented"
264
260
265−intersectRefSchema ::
261+intersectRefSchema
266− PartitionLocation ->
262+ :: PartitionLocation
267− PartitionChoice ->
263+ -> PartitionChoice
268− Traced (Referenced Schema) ->
264+ -> Traced (Referenced Schema)
269− IntersectionM (Referenced Schema)
265+ -> IntersectionM (Referenced Schema)
270intersectRefSchema loc part rs = do
266intersectRefSchema loc part rs = do
271 defs <- R.ask
267 defs <- R.ask
272 Inline <$> intersectSchema loc part (dereference defs rs)
268 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
1+module OpenAPI.Checker.Validate.Schema.Process
2− ( schemaToFormula,
2+ ( schemaToFormula
3 )
3 )
4where
4where
5
5
6import Algebra.Lattice
6import Algebra.Lattice
7−import Control.Monad.Reader hiding (ask)
8import qualified Control.Monad.Reader as R
7import qualified Control.Monad.Reader as R
8+import Control.Monad.Reader hiding (ask)
9import Control.Monad.State
9import Control.Monad.State
10import Control.Monad.Writer
10import Control.Monad.Writer
11import qualified Data.Aeson as A
11import qualified Data.Aeson as A
@@ -13,23 +13,21 @@ import Data.Functor.Identity
13import qualified Data.HashMap.Strict.InsOrd as IOHM
13import qualified Data.HashMap.Strict.InsOrd as IOHM
14import qualified Data.Map as M
14import qualified Data.Map as M
15import Data.Maybe
15import Data.Maybe
16−import Data.Monoid
17import Data.OpenApi hiding (get)
16import 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
30import Data.Ord
17import Data.Ord
31import qualified Data.Set as S
18import 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
33
31
34-- | A fake writer monad that doesn't actually record anything and allows lazy recursion.
32-- | A fake writer monad that doesn't actually record anything and allows lazy recursion.
35newtype Silent w a = Silent {runSilent :: a}
33newtype Silent w a = Silent {runSilent :: a}
@@ -41,11 +39,15 @@ instance Monoid w => MonadWriter w (Silent w) where
41 listen (Silent x) = Silent (x, mempty)
39 listen (Silent x) = Silent (x, mempty)
42 pass (Silent (x, _)) = Silent x
40 pass (Silent (x, _)) = Silent x
43
41
42+instance MonadState (MemoState ()) (Silent w) where
43+ get = Silent $ runIdentity $ runMemo () get
44+ put _ = pure ()
45+
44type ProcessM = StateT (MemoState ()) (ReaderT (Traced (Definitions Schema)) (Writer (P.PathsPrefixTree Behave AnIssue 'SchemaLevel)))
46type ProcessM = StateT (MemoState ()) (ReaderT (Traced (Definitions Schema)) (Writer (P.PathsPrefixTree Behave AnIssue 'SchemaLevel)))
45
47
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))
47
49
48−-- Either ProcessM or SilentM
50+-- Either SilentM or ProcessM
49type MonadProcess m =
51type MonadProcess m =
50 ( MonadReader (Traced (Definitions Schema)) m
52 ( MonadReader (Traced (Definitions Schema)) m
51 , MonadWriter (P.PathsPrefixTree Behave AnIssue 'SchemaLevel) m
53 , MonadWriter (P.PathsPrefixTree Behave AnIssue 'SchemaLevel) m
@@ -55,11 +57,11 @@ type MonadProcess m =
55warn :: MonadProcess m => Issue 'SchemaLevel -> m ()
57warn :: MonadProcess m => Issue 'SchemaLevel -> m ()
56warn issue = tell $ P.singleton $ AnItem Root $ anIssue issue
58warn issue = tell $ P.singleton $ AnItem Root $ anIssue issue
57
59
58−-- Perform a computation lazily, ignoring the warnings and discarding memoization/loop detection information.
60+-- | Ignore warnings but allow a recursive loop that lazily computes a recursive 'Condition'.
59−lazily :: MonadProcess m => SilentM a -> m a
61+silently :: MonadProcess m => SilentM a -> m a
60−lazily m = do
62+silently m = do
61 defs <- R.ask
63 defs <- R.ask
62− pure $ runSilent $ runReaderT (runMemo () m) defs
64+ pure . runSilent $ runReaderT m defs
63
65
64warnKnot :: MonadProcess m => KnotTier (ForeachType JsonFormula) () m
66warnKnot :: MonadProcess m => KnotTier (ForeachType JsonFormula) () m
65warnKnot =
67warnKnot =
@@ -69,10 +71,10 @@ warnKnot =
69 , tieKnot = \_ -> pure
71 , tieKnot = \_ -> pure
70 }
72 }
71
73
72−processRefSchema ::
74+processRefSchema
73− MonadProcess m =>
75+ :: MonadProcess m
74− Traced (Referenced Schema) ->
76+ => Traced (Referenced Schema)
75− m (ForeachType JsonFormula)
77+ -> m (ForeachType JsonFormula)
76processRefSchema x = do
78processRefSchema x = do
77 defs <- R.ask
79 defs <- R.ask
78 memoWithKnot warnKnot (processSchema $ dereference defs x) (ask x)
80 memoWithKnot warnKnot (processSchema $ dereference defs x) (ask x)
@@ -80,10 +82,10 @@ processRefSchema x = do
80-- | Turn a schema into a tuple of 'JsonFormula's that describes the condition
82-- | Turn a schema into a tuple of 'JsonFormula's that describes the condition
81-- for every possible type of a JSON value. The conditions are independent, and
83-- for every possible type of a JSON value. The conditions are independent, and
82-- are thus checked independently.
84-- are thus checked independently.
83−processSchema ::
85+processSchema
84− MonadProcess m =>
86+ :: MonadProcess m
85− Traced Schema ->
87+ => Traced Schema
86− m (ForeachType JsonFormula)
88+ -> m (ForeachType JsonFormula)
87processSchema sch@(extract -> Schema {..}) = do
89processSchema sch@(extract -> Schema {..}) = do
88 let singletonFormula :: Condition t -> JsonFormula t
90 let singletonFormula :: Condition t -> JsonFormula t
89 singletonFormula = JsonFormula . LiteralDNF
91 singletonFormula = JsonFormula . LiteralDNF
@@ -241,11 +243,11 @@ processSchema sch@(extract -> Schema {..}) = do
241 itemsClause <- case tracedItems sch of
243 itemsClause <- case tracedItems sch of
242 Nothing -> pure top
244 Nothing -> pure top
243 Just (Left rs) -> do
245 Just (Left rs) -> do
244− f <- lazily $ processRefSchema rs
246+ f <- silently $ processRefSchema rs
245 pure top {forArray = singletonFormula $ Items f rs}
247 pure top {forArray = singletonFormula $ Items f rs}
246 Just (Right rss) -> do
248 Just (Right rss) -> do
247 fsrs <- forM rss $ \rs -> do
249 fsrs <- forM rss $ \rs -> do
248− f <- lazily $ processRefSchema rs
250+ f <- silently $ processRefSchema rs
249 pure (f, rs)
251 pure (f, rs)
250 pure top {forArray = singletonFormula $ TupleItems fsrs}
252 pure top {forArray = singletonFormula $ TupleItems fsrs}
251
253
@@ -271,12 +273,12 @@ processSchema sch@(extract -> Schema {..}) = do
271 _ -> top
273 _ -> top
272
274
273 (addProps, addPropSchema) <- case tracedAdditionalProperties sch of
275 (addProps, addPropSchema) <- case tracedAdditionalProperties sch of
274− Just (Right rs) -> (,Just rs) <$> lazily (processRefSchema rs)
276+ Just (Right rs) -> (,Just rs) <$> silently (processRefSchema rs)
275 Just (Left False) -> pure (bottom, Nothing)
277 Just (Left False) -> pure (bottom, Nothing)
276 _ -> pure (top, Just $ traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty)
278 _ -> pure (top, Just $ traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty)
277 propList <- forM (S.toList . S.fromList $ IOHM.keys _schemaProperties <> _schemaRequired) $ \k -> do
279 propList <- forM (S.toList . S.fromList $ IOHM.keys _schemaProperties <> _schemaRequired) $ \k -> do
278 (f, psch) <- case IOHM.lookup k $ tracedProperties sch of
280 (f, psch) <- case IOHM.lookup k $ tracedProperties sch of
279− Just rs -> (,rs) <$> lazily (processRefSchema rs)
281+ Just rs -> (,rs) <$> silently (processRefSchema rs)
280 Nothing ->
282 Nothing ->
281 let fakeSchema = traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty
283 let fakeSchema = traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty
282 in -- The mempty here is incorrect, but if addPropSchema was Nothing, then
284 in -- The mempty here is incorrect, but if addPropSchema was Nothing, then
@@ -329,27 +331,26 @@ processSchema sch@(extract -> Schema {..}) = do
329 pure $
331 pure $
330 nullableClause
332 nullableClause
331 \/ meets
333 \/ meets
332− ( allClauses
334+ (allClauses
333− <> [ anyClause
335+ <> [ anyClause
334− , oneClause
336+ , oneClause
335− , typeClause
337+ , typeClause
336− , enumClause
338+ , enumClause
337− , maximumClause
339+ , maximumClause
338− , minimumClause
340+ , minimumClause
339− , multipleOfClause
341+ , multipleOfClause
340− , formatClause
342+ , formatClause
341− , maxLengthClause
343+ , maxLengthClause
342− , minLengthClause
344+ , minLengthClause
343− , patternClause
345+ , patternClause
344− , itemsClause
346+ , itemsClause
345− , maxItemsClause
347+ , maxItemsClause
346− , minItemsClause
348+ , minItemsClause
347− , uniqueItemsClause
349+ , uniqueItemsClause
348− , propertiesClause
350+ , propertiesClause
349− , maxPropertiesClause
351+ , maxPropertiesClause
350− , minPropertiesClause
352+ , minPropertiesClause
351− ]
353+ ])
352− )
353
354
354{- TODO: ReadOnly/WriteOnly #68 -}
355{- TODO: ReadOnly/WriteOnly #68 -}
355
356
@@ -368,8 +369,8 @@ checkOneOfDisjoint schs = do
368runProcessM :: Traced (Definitions Schema) -> ProcessM a -> (a, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
369runProcessM :: Traced (Definitions Schema) -> ProcessM a -> (a, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
369runProcessM defs = runWriter . (`runReaderT` defs) . runMemo ()
370runProcessM defs = runWriter . (`runReaderT` defs) . runMemo ()
370
371
371−schemaToFormula ::
372+schemaToFormula
372− Traced (Definitions Schema) ->
373+ :: Traced (Definitions Schema)
373− Traced Schema ->
374+ -> Traced Schema
374− (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
375+ -> (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
375schemaToFormula defs rs = runProcessM defs $ processSchema rs
376schemaToFormula 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 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.Schema.Traced
3+module OpenAPI.Checker.Validate.Schema.Traced
4− ( Step (..),
4+ ( Step (..)
5− tracedAllOf,
5+ , tracedAllOf
6− tracedAnyOf,
6+ , tracedAnyOf
7− tracedOneOf,
7+ , tracedOneOf
8− tracedItems,
8+ , tracedItems
9− tracedAdditionalProperties,
9+ , tracedAdditionalProperties
10− tracedDiscriminator,
10+ , tracedDiscriminator
11− tracedProperties,
11+ , tracedProperties
12− tracedConjunct,
12+ , tracedConjunct
13− PartitionLocation (..),
13+ , PartitionLocation (..)
14− PartitionChoice (..),
14+ , PartitionChoice (..)
15− Partition,
15+ , Partition
16 )
16 )
17where
17where
18
18
@@ -21,9 +21,9 @@ import Data.Functor
21import qualified Data.HashMap.Strict.InsOrd as IOHM
21import qualified Data.HashMap.Strict.InsOrd as IOHM
22import qualified Data.List.NonEmpty as NE
22import qualified Data.List.NonEmpty as NE
23import Data.OpenApi
23import Data.OpenApi
24−import Data.OpenApi.Compare.Subtree
25import qualified Data.Set as S
24import qualified Data.Set as S
26import Data.Text (Text)
25import Data.Text (Text)
26+import OpenAPI.Checker.Subtree
27
27
28data PartitionChoice
28data PartitionChoice
29 = CByEnumValue (S.Set A.Value)
29 = 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
1+module OpenAPI.Checker.Validate.Schema.TypedJson
2− ( JsonType (..),
2+ ( JsonType (..)
3− describeJSONType,
3+ , describeJSONType
4− TypedValue (..),
4+ , TypedValue (..)
5− untypeValue,
5+ , untypeValue
6− ForeachType (..),
6+ , ForeachType (..)
7− foldType,
7+ , foldType
8− forType_,
8+ , forType_
9 )
9 )
10where
10where
11
11
@@ -88,26 +88,24 @@ forType_ :: Applicative m => (forall x. Typeable x => JsonType -> (ForeachType f
88forType_ k = getAp $ foldType (\ty proj -> Ap $ k ty proj)
88forType_ k = getAp $ foldType (\ty proj -> Ap $ k ty proj)
89
89
90broadcastType :: (forall x. Typeable x => f x) -> ForeachType f
90broadcastType :: (forall x. Typeable x => f x) -> ForeachType f
91−broadcastType k =
91+broadcastType k = ForeachType
92− ForeachType
93− { forNull = k
92+ { forNull = k
94− , forBoolean = k
93+ , forBoolean = k
95− , forNumber = k
94+ , forNumber = k
96− , forString = k
95+ , forString = k
97− , forArray = k
96+ , forArray = k
98− , forObject = k
97+ , forObject = k
99− }
98+ }
100
99
101zipType :: (forall x. Typeable x => f x -> g x -> h x) -> ForeachType f -> ForeachType g -> ForeachType h
100zipType :: (forall x. Typeable x => f x -> g x -> h x) -> ForeachType f -> ForeachType g -> ForeachType h
102−zipType k f1 f2 =
101+zipType k f1 f2 = ForeachType
103− ForeachType
104− { forNull = k (forNull f1) (forNull f2)
102+ { forNull = k (forNull f1) (forNull f2)
105− , forBoolean = k (forBoolean f1) (forBoolean f2)
103+ , forBoolean = k (forBoolean f1) (forBoolean f2)
106− , forNumber = k (forNumber f1) (forNumber f2)
104+ , forNumber = k (forNumber f1) (forNumber f2)
107− , forString = k (forString f1) (forString f2)
105+ , forString = k (forString f1) (forString f2)
108− , forArray = k (forArray f1) (forArray f2)
106+ , forArray = k (forArray f1) (forArray f2)
109− , forObject = k (forObject f1) (forObject f2)
107+ , forObject = k (forObject f1) (forObject f2)
110− }
108+ }
111
109
112instance (forall x. Lattice (f x)) => Lattice (ForeachType f) where
110instance (forall x. Lattice (f x)) => Lattice (ForeachType f) where
113 (\/) = zipType (\/)
111 (\/) = zipType (\/)
src/Data/OpenApi/Compare/Validate/SecurityRequirement.hs → src/OpenAPI/Checker/Validate/SecurityRequirement.hsrenamed+13−14
@@ -1,8 +1,8 @@
1{-# LANGUAGE PartialTypeSignatures #-}
1{-# LANGUAGE PartialTypeSignatures #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
3
3
4−module Data.OpenApi.Compare.Validate.SecurityRequirement
4+module OpenAPI.Checker.Validate.SecurityRequirement
5− ( Issue (..),
5+ ( Issue (..)
6 )
6 )
7where
7where
8
8
@@ -17,14 +17,14 @@ import Data.HList
17import qualified Data.HashMap.Strict.InsOrd as IOHM
17import qualified Data.HashMap.Strict.InsOrd as IOHM
18import qualified Data.List.NonEmpty as NE
18import qualified Data.List.NonEmpty as NE
19import Data.OpenApi
19import 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 ()
24import Data.Set (Set)
20import Data.Set (Set)
25import qualified Data.Set as S
21import qualified Data.Set as S
26import Data.Text (Text)
22import Data.Text (Text)
27import Data.Traversable
23import Data.Traversable
24+import OpenAPI.Checker.Behavior
25+import OpenAPI.Checker.Subtree
26+import OpenAPI.Checker.Validate.OAuth2Flows
27+import OpenAPI.Checker.Validate.SecurityScheme ()
28
28
29instance Subtree SecurityRequirement where
29instance Subtree SecurityRequirement where
30 type SubtreeLevel SecurityRequirement = 'SecurityRequirementLevel
30 type SubtreeLevel SecurityRequirement = 'SecurityRequirementLevel
@@ -41,19 +41,18 @@ instance Subtree SecurityRequirement where
41 for (IOHM.toList $ getSecurityRequirement sec) $ \(key, scopes) ->
41 for (IOHM.toList $ getSecurityRequirement sec) $ \(key, scopes) ->
42 (,scopes) <$> lookupScheme key defs
42 (,scopes) <$> lookupScheme key defs
43 structuralMaybeWith
43 structuralMaybeWith
44− ( \pc' -> do
44+ (\pc' -> do
45− let ProdCons pScopes cScopes = fmap snd <$> pc'
45+ let ProdCons pScopes cScopes = fmap snd <$> pc'
46− unless (pScopes == cScopes) structuralIssue
46+ unless (pScopes == cScopes) structuralIssue
47− structuralList env $ fmap fst <$> pc'
47+ structuralList env $ fmap fst <$> pc'
48− pure ()
48+ pure ())
49− )
50 normalized
49 normalized
51 pure ()
50 pure ()
52 checkSemanticCompatibility env bhv' pc = do
51 checkSemanticCompatibility env bhv' pc = do
53 let schemes = getH @(ProdCons (Traced (Definitions SecurityScheme))) env
52 let schemes = getH @(ProdCons (Traced (Definitions SecurityScheme))) env
54 ( ProdCons pErrs cErrs
53 ( ProdCons pErrs cErrs
55− , (ProdCons pSchemes cSchemes) ::
54+ , (ProdCons pSchemes cSchemes)
56− ProdCons [(Behavior 'SecuritySchemeLevel, Traced SecurityScheme, [Text])]
55+ :: ProdCons [(Behavior 'SecuritySchemeLevel, Traced SecurityScheme, [Text])]
57 ) =
56 ) =
58 NE.unzip $
57 NE.unzip $
59 partitionEithers <$> do
58 partitionEithers <$> do
src/Data/OpenApi/Compare/Validate/SecurityScheme.hs → src/OpenAPI/Checker/Validate/SecurityScheme.hsrenamed+5−5
@@ -1,16 +1,16 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.SecurityScheme
3+module OpenAPI.Checker.Validate.SecurityScheme
4 (
4 (
5 )
5 )
6where
6where
7
7
8import Control.Monad
8import Control.Monad
9import Data.OpenApi
9import Data.OpenApi
10−import Data.OpenApi.Compare.Behavior
10+import OpenAPI.Checker.Behavior
11−import Data.OpenApi.Compare.Orphans ()
11+import OpenAPI.Checker.Orphans ()
12−import Data.OpenApi.Compare.Subtree
12+import OpenAPI.Checker.Subtree
13−import Data.OpenApi.Compare.Validate.OAuth2Flows
13+import OpenAPI.Checker.Validate.OAuth2Flows
14
14
15instance Subtree SecurityScheme where
15instance Subtree SecurityScheme where
16 type CheckEnv SecurityScheme = '[]
16 type CheckEnv SecurityScheme = '[]
src/Data/OpenApi/Compare/Validate/Server.hs → src/OpenAPI/Checker/Validate/Server.hsrenamed+18−21
@@ -1,7 +1,7 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3−module Data.OpenApi.Compare.Validate.Server
3+module OpenAPI.Checker.Validate.Server
4− ( Issue (..),
4+ ( Issue (..)
5 )
5 )
6where
6where
7
7
@@ -19,21 +19,21 @@ import qualified Data.HashSet.InsOrd as IOHM
19import qualified Data.HashSet.InsOrd as IOHS
19import qualified Data.HashSet.InsOrd as IOHS
20import Data.Maybe
20import Data.Maybe
21import Data.OpenApi
21import 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
27import qualified Data.Set as S
22import qualified Data.Set as S
28import Data.Text (Text)
23import Data.Text (Text)
29import qualified Data.Text as T
24import qualified Data.Text as T
30import Data.Traversable
25import 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
31import Text.Pandoc.Builder
31import Text.Pandoc.Builder
32import Prelude as P
32import Prelude as P
33
33
34−tracedParsedServerUrlParts ::
34+tracedParsedServerUrlParts
35− Server ->
35+ :: Server
36− Either (Issue 'ServerLevel) ProcessedServer
36+ -> Either (Issue 'ServerLevel) ProcessedServer
37tracedParsedServerUrlParts s =
37tracedParsedServerUrlParts s =
38 let parsedUrl = parseServerUrl $ _serverUrl s
38 let parsedUrl = parseServerUrl $ _serverUrl s
39 lookupVar var = case IOHM.lookup var (_serverVariables s) of
39 lookupVar var = case IOHM.lookup var (_serverVariables s) of
@@ -46,7 +46,7 @@ instance Behavable 'OperationLevel 'ServerLevel where
46 = InServer Text
46 = InServer Text
47 deriving stock (Eq, Ord, Show)
47 deriving stock (Eq, Ord, Show)
48
48
49− describeBehavior (InServer n) = "Server " <> code n
49+ describeBehaviour (InServer n) = "Server " <> code n
50
50
51instance Subtree [Server] where
51instance Subtree [Server] where
52 type SubtreeLevel [Server] = 'OperationLevel
52 type SubtreeLevel [Server] = 'OperationLevel
@@ -63,12 +63,11 @@ instance Subtree [Server] where
63 pcServer
63 pcServer
64 <&> partitionEithers
64 <&> partitionEithers
65 . fmap
65 . fmap
66− ( \(Traced t s) ->
66+ (\(Traced t s) ->
67− let bhv = beh >>> step (InServer $ _serverUrl s)
67+ let bhv = beh >>> step (InServer $ _serverUrl s)
68− in case tracedParsedServerUrlParts s of
68+ in case tracedParsedServerUrlParts s of
69− Left e -> Left $ issueAt bhv e
69+ Left e -> Left $ issueAt bhv e
70− Right u -> Right (bhv, Traced (t >>> step (ServerStep $ _serverUrl s)) u)
70+ Right u -> Right (bhv, Traced (t >>> step (ServerStep $ _serverUrl s)) u))
71− )
72 . sequence
71 . sequence
73 sequenceA_ pErrs
72 sequenceA_ pErrs
74 sequenceA_ cErrs
73 sequenceA_ cErrs
@@ -102,7 +101,7 @@ data ServerUrlPart var
102 deriving stock (Eq, Show, Functor, Foldable, Traversable)
101 deriving stock (Eq, Show, Functor, Foldable, Traversable)
103
102
104-- | This is super rough. Things like @{a|b}c@ will not match @ac@.
103-- | 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
106--
105--
107-- NOTE: syntax is defined vaguely in the spec.
106-- NOTE: syntax is defined vaguely in the spec.
108parseServerUrl :: Text -> [ServerUrlPart Text]
107parseServerUrl :: Text -> [ServerUrlPart Text]
@@ -134,9 +133,7 @@ instance Issuable 'ServerLevel where
134 | ServerVariableNotDefined Text
133 | ServerVariableNotDefined Text
135 | ServerNotMatched
134 | ServerNotMatched
136 deriving stock (Eq, Ord, Show)
135 deriving stock (Eq, Ord, Show)
137− issueKind = \case
136+ issueIsUnsupported _ = False
138− ServerVariableNotDefined _ -> SchemaInvalid
139− _ -> CertainIssue
140 describeIssue Forward (EnumValueNotConsumed _ v) =
137 describeIssue Forward (EnumValueNotConsumed _ v) =
141 para $ "Enum value " <> code v <> " has been removed."
138 para $ "Enum value " <> code v <> " has been removed."
142 describeIssue Backward (EnumValueNotConsumed _ v) =
139 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
1+resolver: lts-18.2
2−
3−packages:
4− - .
5
2
6extra-deps:
3extra-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
12 - open-union-0.4.0.0
4 - open-union-0.4.0.0
13− - th-abstraction-0.7.0.0
14 - type-fun-0.1.3
5 - 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
19
6
20−nix:
7+apply-ghc-options: everything
21−# packages: [pkg-config yq zlib]
8+rebuild-ghc-options: true
22− shell-file: ./nix/shell.nix
23− pure: false
stack.yaml.lock+4−25
@@ -18,30 +18,9 @@ packages:
18 sha256: b977d42525f0b4223959918d18e7905085283e937493c395077b608cd19b42c1
18 sha256: b977d42525f0b4223959918d18e7905085283e937493c395077b608cd19b42c1
19 original:
19 original:
20 hackage: type-fun-0.1.3
20 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
42snapshots:
21snapshots:
43- completed:
22- completed:
44− size: 587393
23+ size: 585392
45− url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/25.yaml
24+ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/2.yaml
46− sha256: 1b74fb5e970497b5aefae56703f1bd44aa648bd1a5ef95c1eb8c29775087e2bf
25+ sha256: 7abb45c0cc5eb349448b66d8753655542d45d387ad26970419282eab3d860724
47− original: lts-18.25
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
30
30
31## Supported feature tests
31## Supported feature tests
32
32
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.
34
34
35The result will be reflected in the generated compatibility matrix.
35The result will be reflected in the generated compatibility matrix.
36
36
test/Spec/Golden/Extra.hs+44−48
@@ -1,8 +1,8 @@
1module Spec.Golden.Extra
1module Spec.Golden.Extra
2− ( getGoldenInputs,
2+ ( getGoldenInputs
3− getGoldenInputsUniform,
3+ , getGoldenInputsUniform
4− goldenInputsTree,
4+ , goldenInputsTree
5− goldenInputsTreeUniform,
5+ , goldenInputsTreeUniform
6 )
6 )
7where
7where
8
8
@@ -19,12 +19,12 @@ data TestInput t
19 | TestInputLeaf TestName t FilePath
19 | TestInputLeaf TestName t FilePath
20 deriving stock (Functor)
20 deriving stock (Functor)
21
21
22−getGoldenInputs ::
22+getGoldenInputs
23− (Each s t (FilePath, FilePath -> IO a) a) =>
23+ :: (Each s t (FilePath, FilePath -> IO a) a)
24− TestName ->
24+ => TestName
25− FilePath ->
25+ -> FilePath
26− s ->
26+ -> s
27− IO (TestInput t)
27+ -> IO (TestInput t)
28getGoldenInputs name filepath inp = do
28getGoldenInputs name filepath inp = do
29 dirs' <- listDirectory filepath >>= filterM (doesDirectoryExist . (filepath </>))
29 dirs' <- listDirectory filepath >>= filterM (doesDirectoryExist . (filepath </>))
30 case dirs' of
30 case dirs' of
@@ -39,55 +39,51 @@ getGoldenInputs name filepath inp = do
39 TestInputNode name
39 TestInputNode name
40 <$> forM dirs (\dir -> getGoldenInputs dir (filepath </> dir) inp)
40 <$> forM dirs (\dir -> getGoldenInputs dir (filepath </> dir) inp)
41
41
42−getGoldenInputsUniform ::
42+getGoldenInputsUniform
43− (Each t h (FilePath, FilePath -> IO a) a) =>
43+ :: (Each t h (FilePath, FilePath -> IO a) a)
44− (Each s t FilePath (FilePath, FilePath -> IO a)) =>
44+ => (Each s t FilePath (FilePath, FilePath -> IO a))
45− TestName ->
45+ => TestName
46− (FilePath -> IO a) ->
46+ -> (FilePath -> IO a)
47− FilePath ->
47+ -> FilePath
48− s ->
48+ -> s
49− IO (TestInput h)
49+ -> IO (TestInput h)
50getGoldenInputsUniform name f filepath inp = getGoldenInputs name filepath $ inp & each %~ (,f)
50getGoldenInputsUniform name f filepath inp = getGoldenInputs name filepath $ inp & each %~ (,f)
51
51
52−goldenInputsTree ::
52+goldenInputsTree
53− (Each s t (FilePath, FilePath -> IO a) a) =>
53+ :: (Each s t (FilePath, FilePath -> IO a) a)
54− TestName ->
54+ => TestName
55− -- | Root path
55+ -> FilePath -- ^ Root path
56− FilePath ->
57− -- | Name of golden file
56+ -> FilePath -- ^ Name of golden file
58− FilePath ->
57+ -> s
59− s ->
60− (t -> IO BSL.ByteString) ->
58+ -> (t -> IO BSL.ByteString)
61− IO TestTree
59+ -> IO TestTree
62−goldenInputsTree name filepath golden inp f =
60+goldenInputsTree name filepath golden inp f = do
63 runTestInputTree golden f <$> getGoldenInputs name filepath inp
61 runTestInputTree golden f <$> getGoldenInputs name filepath inp
64
62
65−runTestInputTree ::
63+runTestInputTree
66− FilePath ->
64+ :: FilePath
67− (t -> IO BSL.ByteString) ->
65+ -> (t -> IO BSL.ByteString)
68− TestInput t ->
66+ -> TestInput t
69− TestTree
67+ -> TestTree
70runTestInputTree golden f (TestInputNode name rest) =
68runTestInputTree golden f (TestInputNode name rest) =
71 testGroup name (runTestInputTree golden f <$> rest)
69 testGroup name (runTestInputTree golden f <$> rest)
72runTestInputTree golden f (TestInputLeaf name t path) =
70runTestInputTree golden f (TestInputLeaf name t path) =
73 goldenVsStringDiff
71 goldenVsStringDiff
74 name
72 name
75− (\ref new -> ["diff-yaml", ref, new])
73+ (\ref new -> ["diff", "-u", ref, new])
76 (path </> golden)
74 (path </> golden)
77 (f t)
75 (f t)
78
76
79−goldenInputsTreeUniform ::
77+goldenInputsTreeUniform
80− ( Each t h (FilePath, FilePath -> IO a) a
78+ :: ( Each t h (FilePath, FilePath -> IO a) a
81− , Each s t FilePath (FilePath, FilePath -> IO a)
79+ , Each s t FilePath (FilePath, FilePath -> IO a)
82− ) =>
80+ )
83− String ->
81+ => String
84− -- | Root path
82+ -> FilePath -- ^ Root path
85− FilePath ->
86− -- | Name of golden file
83+ -> FilePath -- ^ Name of golden file
87− FilePath ->
84+ -> s
88− s ->
89− (FilePath -> IO a) ->
85+ -> (FilePath -> IO a)
90− (h -> IO BSL.ByteString) ->
86+ -> (h -> IO BSL.ByteString)
91− IO TestTree
87+ -> IO TestTree
92goldenInputsTreeUniform name filepath golden inp h =
88goldenInputsTreeUniform name filepath golden inp h =
93 goldenInputsTree name filepath golden (inp & each %~ (,h))
89 goldenInputsTree name filepath golden (inp & each %~ (,h))
test/Spec/Golden/TraceTree.hs+6−7
@@ -1,5 +1,5 @@
1module Spec.Golden.TraceTree
1module Spec.Golden.TraceTree
2− ( tests,
2+ ( tests
3 )
3 )
4where
4where
5
5
@@ -7,14 +7,13 @@ import Control.Category
7import Control.Exception
7import Control.Exception
8import qualified Data.ByteString.Lazy as BSL
8import qualified Data.ByteString.Lazy as BSL
9import Data.Default
9import Data.Default
10−import Data.OpenApi.Compare.Run
11−import Data.OpenApi.Compare.Validate.OpenApi ()
12import Data.Text (Text)
10import Data.Text (Text)
13import qualified Data.Text.Encoding as T
11import qualified Data.Text.Encoding as T
14import qualified Data.Yaml as Yaml
12import qualified Data.Yaml as Yaml
13+import OpenAPI.Checker.Run
14+import OpenAPI.Checker.Validate.OpenApi ()
15import Spec.Golden.Extra
15import Spec.Golden.Extra
16import Test.Tasty (TestTree, testGroup)
16import Test.Tasty (TestTree, testGroup)
17−import Text.Pandoc.Builder
18import Text.Pandoc.Class
17import Text.Pandoc.Class
19import Text.Pandoc.Options
18import Text.Pandoc.Options
20import Text.Pandoc.Writers
19import Text.Pandoc.Writers
@@ -25,7 +24,7 @@ tests = do
25 traceTreeTests <-
24 traceTreeTests <-
26 goldenInputsTreeUniform
25 goldenInputsTreeUniform
27 "TraceTree"
26 "TraceTree"
28− "test/golden"
27+ "test/golden/common"
29 "trace-tree.yaml"
28 "trace-tree.yaml"
30 ("a.yaml", "b.yaml")
29 ("a.yaml", "b.yaml")
31 Yaml.decodeFileThrow
30 Yaml.decodeFileThrow
@@ -34,11 +33,11 @@ tests = do
34 reportTests <-
33 reportTests <-
35 goldenInputsTreeUniform
34 goldenInputsTreeUniform
36 "Report"
35 "Report"
37− "test/golden"
36+ "test/golden/common"
38 "report.md"
37 "report.md"
39 ("a.yaml", "b.yaml")
38 ("a.yaml", "b.yaml")
40 Yaml.decodeFileThrow
39 Yaml.decodeFileThrow
41− (runPandoc . writeMarkdown def {writerExtensions = githubMarkdownExtensions} . doc . fst . runReport def)
40+ (runPandoc . writeMarkdown def {writerExtensions = githubMarkdownExtensions} . fst . runReport def)
42
41
43 return $ testGroup "Golden tests" [traceTreeTests, reportTests]
42 return $ testGroup "Golden tests" [traceTreeTests, reportTests]
44
43
test/golden/common/bad-oneof-unchanged/report.md+6−6
@@ -1,19 +1,19 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes | [❓ Unsupported feature changes](#unsupported-changes) |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | [🤷 Unsupported feature changes](#unsupported-changes) |
4−|---------------------|-------------------------|--------------------------------------------------------|
4+|---------------------|------------------------|-------------------------------------------------------|
5−| 0 | 0 | 2 |
5+| 0 | 0 | 2 |
6
6
7−# <span id="unsupported-changes"></span>❓ Unsupported feature changes
7+# <span id="unsupported-changes"></span>🤷 Unsupported feature changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13Could not deduce that `oneOf` cases don't overlap. Treating the `oneOf`
13Could not deduce that `oneOf` cases don't overlap. Treating the `oneOf`
14as an `anyOf`. Reported errors might not be accurate.
14as an `anyOf`. Reported errors might not be accurate.
15
15
16−### ⬅️☁️ JSON Response – 200
16+### 📱⬅️ JSON Response – 200
17
17
18Could not deduce that `oneOf` cases don't overlap. Treating the `oneOf`
18Could not deduce that `oneOf` cases don't overlap. Treating the `oneOf`
19as an `anyOf`. Reported errors might not be accurate.
19as 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:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -7,7 +7,7 @@ forwardChanges:
7 WithStatusCode 200:
7 WithStatusCode 200:
8 ResponsePayload:
8 ResponsePayload:
9 PayloadSchema: OneOfNotDisjoint
9 PayloadSchema: OneOfNotDisjoint
10−backwardChanges:
10+nonBreakingChanges:
11 AtPath "/test":
11 AtPath "/test":
12 InOperation PostMethod:
12 InOperation PostMethod:
13 InRequest:
13 InRequest:
test/golden/common/enum-added/report.md+7−7
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
12
12
13#### `$(String)`
13#### `$(String)`
14
14
@@ -18,11 +18,11 @@ The following enum value was added:
18"C"
18"C"
19```
19```
20
20
21−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
21+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
22
22
23## **POST** /test
23## **POST** /test
24
24
25−### ➡️☁️ JSON Request
25+### 📱➡️ JSON Request
26
26
27#### `$(String)`
27#### `$(String)`
28
28
test/golden/common/enum-added/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
5 ResponsePayload:
5 ResponsePayload:
6 PayloadSchema:
6 PayloadSchema:
7 OfType String: EnumDoesntSatisfy (String "C")
7 OfType String: EnumDoesntSatisfy (String "C")
8−backwardChanges:
8+nonBreakingChanges:
9 AtPath "/test":
9 AtPath "/test":
10 InOperation PostMethod:
10 InOperation PostMethod:
11 InRequest:
11 InRequest:
test/golden/common/enum-anyof/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
5+| 0 | 0 | 0 |
test/golden/common/enum-anyof/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/id/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
5+| 0 | 0 | 0 |
test/golden/common/id/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/json/recursive/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
5+| 0 | 0 | 0 |
test/golden/common/json/recursive/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/maximum-lowered/report.md+7−7
@@ -1,24 +1,24 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$(Number)`
13#### `$(Number)`
14
14
15Upper bound changed from 3.0 inclusive to 2.0 inclusive.
15Upper bound changed from 3.0 inclusive to 2.0 inclusive.
16
16
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
18
18
19## **POST** /test
19## **POST** /test
20
20
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
22
22
23#### `$(Number)`
23#### `$(Number)`
24
24
test/golden/common/maximum-lowered/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -6,7 +6,7 @@ forwardChanges:
6 PayloadSchema:
6 PayloadSchema:
7 OfType Number: MatchingMaximumWeak (ProdCons {producer = Inclusive 3.0,
7 OfType Number: MatchingMaximumWeak (ProdCons {producer = Inclusive 3.0,
8 consumer = Inclusive 2.0})
8 consumer = Inclusive 2.0})
9−backwardChanges:
9+nonBreakingChanges:
10 AtPath "/test":
10 AtPath "/test":
11 InOperation PostMethod:
11 InOperation PostMethod:
12 WithStatusCode 200:
12 WithStatusCode 200:
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InParam "test1": ParamEmptinessIncompatible
4 InParam "test1": ParamEmptinessIncompatible
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod:
4 InOperation PostMethod:
5 InParam "test1": ParamEmptinessIncompatible
5 InParam "test1": ParamEmptinessIncompatible
test/golden/common/pathItem/operation/parameters/change/report.md+5−5
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
@@ -16,7 +16,7 @@
16
16
17The type has been removed.
17The type has been removed.
18
18
19−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
19+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
20
20
21## **POST** /test
21## **POST** /test
22
22
test/golden/common/pathItem/operation/parameters/change/trace-tree.yaml+2−2
@@ -1,10 +1,10 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InParam "test":
4 InParam "test":
5 InParamSchema:
5 InParamSchema:
6 OfType String: TypeBecomesEmpty
6 OfType String: TypeBecomesEmpty
7−backwardChanges:
7+nonBreakingChanges:
8 AtPath "/test":
8 AtPath "/test":
9 InOperation PostMethod:
9 InOperation PostMethod:
10 InParam "test":
10 InParam "test":
test/golden/common/pathItem/operation/parameters/required/false/add/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
5+| 0 | 0 | 0 |
test/golden/common/pathItem/operation/parameters/required/false/add/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/false/del/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
5+| 0 | 0 | 0 |
test/golden/common/pathItem/operation/parameters/required/false/del/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/reset/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/parameters/required/reset/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod:
4 InOperation PostMethod:
5 InParam "test1": ParamRequired
5 InParam "test1": ParamRequired
test/golden/common/pathItem/operation/parameters/required/set/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/parameters/required/set/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InParam "test1": ParamRequired
4 InParam "test1": ParamRequired
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/parameters/required/true/add/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/parameters/required/true/add/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod: ParamNotMatched "test2"
3 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 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/parameters/required/true/del/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod: ParamNotMatched "test2"
4 InOperation PostMethod: ParamNotMatched "test2"
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod:
4 InOperation PostMethod:
5 InRequest: RequestMediaTypeNotFound application/x-www-form-urlencoded
5 InRequest: RequestMediaTypeNotFound application/x-www-form-urlencoded
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/report.md+7−7
@@ -1,24 +1,24 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$(String)`
13#### `$(String)`
14
14
15The type has been removed.
15The type has been removed.
16
16
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
18
18
19## **POST** /test
19## **POST** /test
20
20
21−### ➡️☁️ JSON Request
21+### 📱➡️ JSON Request
22
22
23#### `$(Number)`
23#### `$(Number)`
24
24
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
5 InPayload:
5 InPayload:
6 PayloadSchema:
6 PayloadSchema:
7 OfType String: TypeBecomesEmpty
7 OfType String: TypeBecomesEmpty
8−backwardChanges:
8+nonBreakingChanges:
9 AtPath "/test":
9 AtPath "/test":
10 InOperation PostMethod:
10 InOperation PostMethod:
11 InRequest:
11 InRequest:
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest: RequestMediaTypeNotFound application/x-www-form-urlencoded
4 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 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/requestBody/required/reset/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod:
4 InOperation PostMethod:
5 InRequest: RequestBodyRequired
5 InRequest: RequestBodyRequired
test/golden/common/pathItem/operation/requestBody/required/set/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/requestBody/required/set/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest: RequestBodyRequired
4 InRequest: RequestBodyRequired
5−backwardChanges: {}
5+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/add/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/responses/add/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
3 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 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod:
4 InOperation PostMethod:
5 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
5 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
4 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 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
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: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/del/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
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: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|------------------------|-------------------------------|
5−| 1 | 0 |
5+| 1 | 0 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200: ConsumerDoesntHaveMediaType application/x-www-form-urlencoded
4 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 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
12
12
13#### `$(Number)`
13#### `$(Number)`
14
14
15The type has been added.
15The type has been added.
16
16
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
18
18
19## **POST** /test
19## **POST** /test
20
20
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
22
22
23#### `$(String)`
23#### `$(String)`
24
24
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
5 ResponsePayload:
5 ResponsePayload:
6 PayloadSchema:
6 PayloadSchema:
7 OfType Number: TypeBecomesEmpty
7 OfType Number: TypeBecomesEmpty
8−backwardChanges:
8+nonBreakingChanges:
9 AtPath "/test":
9 AtPath "/test":
10 InOperation PostMethod:
10 InOperation PostMethod:
11 WithStatusCode 200:
11 WithStatusCode 200:
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/trace-tree.yaml+2−2
@@ -1,5 +1,5 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod:
4 InOperation PostMethod:
5 WithStatusCode 200: ConsumerDoesntHaveMediaType application/x-www-form-urlencoded
5 WithStatusCode 200: ConsumerDoesntHaveMediaType application/x-www-form-urlencoded
test/golden/common/pathItem/operation/responses/del/report.md+4−4
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| ⚠️ Breaking changes | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|---------------------|--------------------------------------------------|
4+|---------------------|-------------------------------------------------|-------------------------------|
5−| 0 | 1 |
5+| 0 | 1 | 0 |
6
6
7−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
7+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
test/golden/common/pathItem/operation/responses/del/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges:
2+nonBreakingChanges:
3 AtPath "/test":
3 AtPath "/test":
4 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
4 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
test/golden/common/property-removed-additional/report.md+7−7
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 3 | 3 |
5+| 3 | 3 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
12
12
13#### `$.property2`
13#### `$.property2`
14
14
@@ -21,11 +21,11 @@
21
21
223. The property may not be present.
223. The property may not be present.
23
23
24−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
24+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
25
25
26## **POST** /test
26## **POST** /test
27
27
28−### ➡️☁️ JSON Request
28+### 📱➡️ JSON Request
29
29
30#### `$.property2`
30#### `$.property2`
31
31
test/golden/common/property-removed-additional/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
@@ -9,7 +9,7 @@ forwardChanges:
9 - TypesRestricted [Number]
9 - TypesRestricted [Number]
10 - AdditionalToProperty
10 - AdditionalToProperty
11 - PropertyNowRequired
11 - PropertyNowRequired
12−backwardChanges:
12+nonBreakingChanges:
13 AtPath "/test":
13 AtPath "/test":
14 InOperation PostMethod:
14 InOperation PostMethod:
15 InRequest:
15 InRequest:
test/golden/common/property-removed/report.md+22−6
@@ -1,21 +1,37 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 2 | 0 |
5+| 2 | 2 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$.property2`
13#### `$.property2`
14
14
15The property has been removed.
15The property has been removed.
16
16
17−### ⬅️☁️ JSON Response – 200
17+### 📱⬅️ JSON Response – 200
18
18
19#### `$.property2`
19#### `$.property2`
20
20
21The property may not be present.
21The 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:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -11,7 +11,7 @@ forwardChanges:
11 PayloadSchema:
11 PayloadSchema:
12 OfType Object:
12 OfType Object:
13 InProperty "property2": PropertyNowRequired
13 InProperty "property2": PropertyNowRequired
14−backwardChanges:
14+nonBreakingChanges:
15 AtPath "/test":
15 AtPath "/test":
16 InOperation PostMethod:
16 InOperation PostMethod:
17 InRequest:
17 InRequest:
test/golden/common/property-required/report.md+7−7
@@ -1,24 +1,24 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$.property2`
13#### `$.property2`
14
14
15The property has become required.
15The property has become required.
16
16
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
18
18
19## **POST** /test
19## **POST** /test
20
20
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
22
22
23#### `$.property2`
23#### `$.property2`
24
24
test/golden/common/property-required/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -6,7 +6,7 @@ forwardChanges:
6 PayloadSchema:
6 PayloadSchema:
7 OfType Object:
7 OfType Object:
8 InProperty "property2": PropertyNowRequired
8 InProperty "property2": PropertyNowRequired
9−backwardChanges:
9+nonBreakingChanges:
10 AtPath "/test":
10 AtPath "/test":
11 InOperation PostMethod:
11 InOperation PostMethod:
12 WithStatusCode 200:
12 WithStatusCode 200:
test/golden/common/random-example/report.md+9−9
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 5 | 6 |
5+| 5 | 6 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **GET** /pets
9## **GET** /pets
10
10
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
12
12
13#### `$[*].name(String)`
13#### `$[*].name(String)`
14
14
@@ -18,7 +18,7 @@
18
18
19## **POST** /pets
19## **POST** /pets
20
20
21−### ➡️☁️ JSON Request
21+### 📱➡️ JSON Request
22
22
23#### `$.weight`
23#### `$.weight`
24
24
@@ -33,7 +33,7 @@
33
33
34Value is now a multiple of 1.0.
34Value is now a multiple of 1.0.
35
35
36−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
36+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
37
37
38## **GET** /pets
38## **GET** /pets
39
39
@@ -45,7 +45,7 @@ Value is now a multiple of 1.0.
45
45
46Upper bound changed from 20.0 inclusive to 30.0 inclusive.
46Upper bound changed from 20.0 inclusive to 30.0 inclusive.
47
47
48−### ⬅️☁️ JSON Response – 200
48+### 📱⬅️ JSON Response – 200
49
49
50#### `$[*].weight`
50#### `$[*].weight`
51
51
@@ -62,7 +62,7 @@ Value is now a multiple of 1.0.
62
62
63## **POST** /pets
63## **POST** /pets
64
64
65−### ➡️☁️ JSON Request
65+### 📱➡️ JSON Request
66
66
67#### `$.name(String)`
67#### `$.name(String)`
68
68
test/golden/common/random-example/trace-tree.yaml+6−6
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/pets":
2 AtPath "/pets":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
@@ -20,13 +20,9 @@ forwardChanges:
20 - TypesRestricted [Number]
20 - TypesRestricted [Number]
21 - AdditionalToProperty
21 - AdditionalToProperty
22 - OfType Number: NoMatchingMultipleOf 1.0
22 - OfType Number: NoMatchingMultipleOf 1.0
23−backwardChanges:
23+nonBreakingChanges:
24 AtPath "/pets":
24 AtPath "/pets":
25 InOperation GetMethod:
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:
26 WithStatusCode 200:
31 ResponsePayload:
27 ResponsePayload:
32 PayloadSchema:
28 PayloadSchema:
@@ -37,6 +33,10 @@ backwardChanges:
37 - TypesRestricted [Number]
33 - TypesRestricted [Number]
38 - AdditionalToProperty
34 - AdditionalToProperty
39 - OfType Number: NoMatchingMultipleOf 1.0
35 - OfType Number: NoMatchingMultipleOf 1.0
36+ InParam "limit":
37+ InParamSchema:
38+ OfType Number: MatchingMaximumWeak (ProdCons {producer = Inclusive 30.0,
39+ consumer = Inclusive 20.0})
40 InOperation PostMethod:
40 InOperation PostMethod:
41 InRequest:
41 InRequest:
42 InPayload:
42 InPayload:
test/golden/common/recursive/report.md+7−7
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **GET** /api/foo
9## **GET** /api/foo
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$.leaf.value.foo(String)`
13#### `$.leaf.value.foo(String)`
14
14
@@ -18,11 +18,11 @@ The following enum value has been added:
18"a"
18"a"
19```
19```
20
20
21−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
21+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
22
22
23## **GET** /api/foo
23## **GET** /api/foo
24
24
25−### ⬅️☁️ JSON Response – 200
25+### 📱⬅️ JSON Response – 200
26
26
27#### `$.leaf.value.foo(String)`
27#### `$.leaf.value.foo(String)`
28
28
test/golden/common/recursive/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/api/foo":
2 AtPath "/api/foo":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 InRequest:
4 InRequest:
@@ -11,7 +11,7 @@ forwardChanges:
11 OfType Object:
11 OfType Object:
12 InProperty "foo":
12 InProperty "foo":
13 OfType String: NoMatchingEnum (String "a")
13 OfType String: NoMatchingEnum (String "a")
14−backwardChanges:
14+nonBreakingChanges:
15 AtPath "/api/foo":
15 AtPath "/api/foo":
16 InOperation GetMethod:
16 InOperation GetMethod:
17 WithStatusCode 200:
17 WithStatusCode 200:
test/golden/common/security-scheme/report.md+5−5
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 2 | 1 |
5+| 2 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /oauth/check
9## **POST** /oauth/check
10
10
@@ -22,7 +22,7 @@ Security scheme has been removed.
22
22
23Security scheme has been removed.
23Security scheme has been removed.
24
24
25−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
25+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
26
26
27## **POST** /oauth/check
27## **POST** /oauth/check
28
28
test/golden/common/security-scheme/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/oauth/sign_out":
2 AtPath "/oauth/sign_out":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 SecurityRequirementStep 1:
4 SecurityRequirementStep 1:
@@ -7,7 +7,7 @@ forwardChanges:
7 InOperation PostMethod:
7 InOperation PostMethod:
8 SecurityRequirementStep 0:
8 SecurityRequirementStep 0:
9 SecuritySchemeStep "oauth": SecuritySchemeNotMatched
9 SecuritySchemeStep "oauth": SecuritySchemeNotMatched
10−backwardChanges:
10+nonBreakingChanges:
11 AtPath "/oauth/check":
11 AtPath "/oauth/check":
12 InOperation PostMethod:
12 InOperation PostMethod:
13 SecurityRequirementStep 0:
13 SecurityRequirementStep 0:
test/golden/common/servers/report.md+5−5
@@ -1,10 +1,10 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 3 | 1 |
5+| 3 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **GET** /pets
9## **GET** /pets
10
10
@@ -18,7 +18,7 @@ The server was removed.
18
18
192. A variable has been changed from being open to being closed.
192. A variable has been changed from being open to being closed.
20
20
21−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
21+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
22
22
23## **GET** /pets
23## **GET** /pets
24
24
test/golden/common/servers/trace-tree.yaml+2−2
@@ -1,11 +1,11 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/pets":
2 AtPath "/pets":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 InServer "http://{x}variable.path/{y}/{openVariable1}/{openVariable2}":
4 InServer "http://{x}variable.path/{y}/{openVariable1}/{openVariable2}":
5 - EnumValueNotConsumed 1 "a"
5 - EnumValueNotConsumed 1 "a"
6 - ConsumerNotOpen 7
6 - ConsumerNotOpen 7
7 InServer "http://missing.url": ServerNotMatched
7 InServer "http://missing.url": ServerNotMatched
8−backwardChanges:
8+nonBreakingChanges:
9 AtPath "/pets":
9 AtPath "/pets":
10 InOperation GetMethod:
10 InOperation GetMethod:
11 InServer "http://{x}variable.path/{y}/{openVariable1}/{openVariable2}": EnumValueNotConsumed
11 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 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 2 | 2 |
5+| 2 | 2 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$[1](String)`
13#### `$[1](String)`
14
14
15The type has been removed.
15The type has been removed.
16
16
17−### ⬅️☁️ JSON Response – 200
17+### 📱⬅️ JSON Response – 200
18
18
19#### `$[1](Object)`
19#### `$[1](Object)`
20
20
21The type has been added.
21The type has been added.
22
22
23−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
23+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
24
24
25## **POST** /test
25## **POST** /test
26
26
27−### ➡️☁️ JSON Request
27+### 📱➡️ JSON Request
28
28
29#### `$[1](Object)`
29#### `$[1](Object)`
30
30
31The type has been added.
31The type has been added.
32
32
33−### ⬅️☁️ JSON Response – 200
33+### 📱⬅️ JSON Response – 200
34
34
35#### `$[1](String)`
35#### `$[1](String)`
36
36
test/golden/common/tuple-items/heterogeneous-component-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -13,7 +13,7 @@ forwardChanges:
13 OfType Array:
13 OfType Array:
14 InItem 1:
14 InItem 1:
15 OfType Object: TypeBecomesEmpty
15 OfType Object: TypeBecomesEmpty
16−backwardChanges:
16+nonBreakingChanges:
17 AtPath "/test":
17 AtPath "/test":
18 InOperation PostMethod:
18 InOperation PostMethod:
19 InRequest:
19 InRequest:
test/golden/common/tuple-items/heterogeneous-length-mismatch/report.md+22−6
@@ -1,20 +1,36 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | ⚠️ Non-breaking changes |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|-------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 2 | 0 |
5+| 2 | 2 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$(Array)`
13#### `$(Array)`
14
14
15Tuple length changed from 3 to 2.
15Tuple length changed from 3 to 2.
16
16
17+### 📱⬅️ JSON Response – 200
18+
19+#### `$(Array)`
20+
21+Tuple length changed from 3 to 2.
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 3 to 2.
32+
17−### ⬅️☁️ JSON Response – 200
33+### 📱⬅️ JSON Response – 200
18
34
19#### `$(Array)`
35#### `$(Array)`
20
36
test/golden/common/tuple-items/heterogeneous-length-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -11,7 +11,7 @@ forwardChanges:
11 PayloadSchema:
11 PayloadSchema:
12 OfType Array: TupleItemsLengthChanged (ProdCons {producer = 2, consumer
12 OfType Array: TupleItemsLengthChanged (ProdCons {producer = 2, consumer
13 = 3})
13 = 3})
14−backwardChanges:
14+nonBreakingChanges:
15 AtPath "/test":
15 AtPath "/test":
16 InOperation PostMethod:
16 InOperation PostMethod:
17 InRequest:
17 InRequest:
test/golden/common/tuple-items/homogeneous-component-mismatch/report.md+17−9
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 4 | 2 |
5+| 4 | 4 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### `$(Array)`
13#### `$(Array)`
14
14
@@ -18,7 +18,7 @@ The array is no longer explicitly defined as a tuple.
18
18
19The type has been removed.
19The type has been removed.
20
20
21−### ⬅️☁️ JSON Response – 200
21+### 📱⬅️ JSON Response – 200
22
22
23#### `$(Array)`
23#### `$(Array)`
24
24
@@ -28,17 +28,25 @@ The array is no longer explicitly defined as a tuple.
28
28
29The type has been added.
29The type has been added.
30
30
31−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
31+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
32
32
33## **POST** /test
33## **POST** /test
34
34
35−### ➡️☁️ JSON Request
35+### 📱➡️ JSON Request
36+
37+#### `$(Array)`
38+
39+The array is no longer explicitly defined as a tuple.
36
40
37#### `$[0](Number)`
41#### `$[0](Number)`
38
42
39The type has been added.
43The type has been added.
40
44
41−### ⬅️☁️ JSON Response – 200
45+### 📱⬅️ JSON Response – 200
46+
47+#### `$(Array)`
48+
49+The array is no longer explicitly defined as a tuple.
42
50
43#### `$[0](String)`
51#### `$[0](String)`
44
52
test/golden/common/tuple-items/homogeneous-component-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -15,7 +15,7 @@ forwardChanges:
15 - ArrayToTuple
15 - ArrayToTuple
16 - InItem 0:
16 - InItem 0:
17 OfType Number: TypeBecomesEmpty
17 OfType Number: TypeBecomesEmpty
18−backwardChanges:
18+nonBreakingChanges:
19 AtPath "/test":
19 AtPath "/test":
20 InOperation PostMethod:
20 InOperation PostMethod:
21 InRequest:
21 InRequest:
test/golden/common/tuple-items/homogeneous-length-mismatch/report.md+13−13
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 5 | 5 |
5+| 5 | 5 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test1
9## **POST** /test1
10
10
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
12
12
13#### `$(Array)`
13#### `$(Array)`
14
14
@@ -16,7 +16,7 @@ The array is no longer explicitly defined as a tuple.
16
16
17## **POST** /test2
17## **POST** /test2
18
18
19−### ⬅️☁️ JSON Response – 200
19+### 📱⬅️ JSON Response – 200
20
20
21#### `$(Array)`
21#### `$(Array)`
22
22
@@ -24,13 +24,13 @@ The array is no longer explicitly defined as a tuple.
24
24
25## **POST** /test3
25## **POST** /test3
26
26
27−### ➡️☁️ JSON Request
27+### 📱➡️ JSON Request
28
28
29#### `$(Array)`
29#### `$(Array)`
30
30
31Minimum length of the array changed from 2 to 3.
31Minimum length of the array changed from 2 to 3.
32
32
33−### ⬅️☁️ JSON Response – 200
33+### 📱⬅️ JSON Response – 200
34
34
35#### `$(Array)`
35#### `$(Array)`
36
36
@@ -38,11 +38,11 @@ Minimum length of the array changed from 2 to 3.
38
38
392. The array is no longer explicitly defined as a tuple.
392. The array is no longer explicitly defined as a tuple.
40
40
41−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
41+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
42
42
43## **POST** /test1
43## **POST** /test1
44
44
45−### ➡️☁️ JSON Request
45+### 📱➡️ JSON Request
46
46
47#### `$(Array)`
47#### `$(Array)`
48
48
@@ -50,7 +50,7 @@ The array is no longer explicitly defined as a tuple.
50
50
51## **POST** /test2
51## **POST** /test2
52
52
53−### ➡️☁️ JSON Request
53+### 📱➡️ JSON Request
54
54
55#### `$(Array)`
55#### `$(Array)`
56
56
@@ -58,7 +58,7 @@ The array is no longer explicitly defined as a tuple.
58
58
59## **POST** /test3
59## **POST** /test3
60
60
61−### ➡️☁️ JSON Request
61+### 📱➡️ JSON Request
62
62
63#### `$(Array)`
63#### `$(Array)`
64
64
@@ -66,7 +66,7 @@ The array is no longer explicitly defined as a tuple.
66
66
672. The array is no longer explicitly defined as a tuple.
672. The array is no longer explicitly defined as a tuple.
68
68
69−### ⬅️☁️ JSON Response – 200
69+### 📱⬅️ JSON Response – 200
70
70
71#### `$(Array)`
71#### `$(Array)`
72
72
test/golden/common/tuple-items/homogeneous-length-mismatch/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test3":
2 AtPath "/test3":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -24,7 +24,7 @@ forwardChanges:
24 ResponsePayload:
24 ResponsePayload:
25 PayloadSchema:
25 PayloadSchema:
26 OfType Array: NoMatchingTupleItems
26 OfType Array: NoMatchingTupleItems
27−backwardChanges:
27+nonBreakingChanges:
28 AtPath "/test3":
28 AtPath "/test3":
29 InOperation PostMethod:
29 InOperation PostMethod:
30 InRequest:
30 InRequest:
test/golden/common/tuple-items/homogeneous-vector/report.md+3−3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3−| ❌ Breaking changes | ⚠️ Non-breaking changes |
3+| ⚠️ Breaking changes | 🙆 Non-breaking changes | 🤷 Unsupported feature changes |
4−|---------------------|-------------------------|
4+|---------------------|------------------------|-------------------------------|
5−| 0 | 0 |
5+| 0 | 0 | 0 |
test/golden/common/tuple-items/homogeneous-vector/trace-tree.yaml+2−2
@@ -1,2 +1,2 @@
1−forwardChanges: {}
1+breakingChanges: {}
2−backwardChanges: {}
2+nonBreakingChanges: {}
test/golden/common/type-changing/report.md+15−15
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 6 | 6 |
5+| 6 | 6 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test1
9## **POST** /test1
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13Values are now limited to the following types:
13Values are now limited to the following types:
14
14
@@ -16,7 +16,7 @@ Values are now limited to the following types:
16
16
17## **POST** /test2
17## **POST** /test2
18
18
19−### ➡️☁️ JSON Request
19+### 📱➡️ JSON Request
20
20
21Values are now limited to the following types:
21Values are now limited to the following types:
22
22
@@ -28,7 +28,7 @@ Values are now limited to the following types:
28
28
29## **POST** /test3
29## **POST** /test3
30
30
31−### ➡️☁️ JSON Request
31+### 📱➡️ JSON Request
32
32
33#### `$(Array)`
33#### `$(Array)`
34
34
@@ -36,7 +36,7 @@ The type has been removed.
36
36
37## **POST** /test4
37## **POST** /test4
38
38
39−### ➡️☁️ JSON Request
39+### 📱➡️ JSON Request
40
40
41#### `$(Object)`
41#### `$(Object)`
42
42
@@ -44,7 +44,7 @@ The type has been removed.
44
44
45## **POST** /test5
45## **POST** /test5
46
46
47−### ➡️☁️ JSON Request
47+### 📱➡️ JSON Request
48
48
49Values are now limited to the following types:
49Values are now limited to the following types:
50
50
@@ -54,11 +54,11 @@ Values are now limited to the following types:
54
54
55Value is now a multiple of 1.0.
55Value is now a multiple of 1.0.
56
56
57−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
57+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
58
58
59## **POST** /test1
59## **POST** /test1
60
60
61−### ⬅️☁️ JSON Response – 200
61+### 📱⬅️ JSON Response – 200
62
62
63Values are now limited to the following types:
63Values are now limited to the following types:
64
64
@@ -66,7 +66,7 @@ Values are now limited to the following types:
66
66
67## **POST** /test2
67## **POST** /test2
68
68
69−### ⬅️☁️ JSON Response – 200
69+### 📱⬅️ JSON Response – 200
70
70
71Values are now limited to the following types:
71Values are now limited to the following types:
72
72
@@ -78,7 +78,7 @@ Values are now limited to the following types:
78
78
79## **POST** /test3
79## **POST** /test3
80
80
81−### ⬅️☁️ JSON Response – 200
81+### 📱⬅️ JSON Response – 200
82
82
83#### `$(Array)`
83#### `$(Array)`
84
84
@@ -86,7 +86,7 @@ The type has been removed.
86
86
87## **POST** /test4
87## **POST** /test4
88
88
89−### ⬅️☁️ JSON Response – 200
89+### 📱⬅️ JSON Response – 200
90
90
91#### `$(Object)`
91#### `$(Object)`
92
92
@@ -94,7 +94,7 @@ The type has been removed.
94
94
95## **POST** /test5
95## **POST** /test5
96
96
97−### ⬅️☁️ JSON Response – 200
97+### 📱⬅️ JSON Response – 200
98
98
99Values are now limited to the following types:
99Values are now limited to the following types:
100
100
test/golden/common/type-changing/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test5":
2 AtPath "/test5":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -28,7 +28,7 @@ forwardChanges:
28 InRequest:
28 InRequest:
29 InPayload:
29 InPayload:
30 PayloadSchema: TypesRestricted [Number,Array,Object]
30 PayloadSchema: TypesRestricted [Number,Array,Object]
31−backwardChanges:
31+nonBreakingChanges:
32 AtPath "/test5":
32 AtPath "/test5":
33 InOperation PostMethod:
33 InOperation PostMethod:
34 WithStatusCode 200:
34 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:
2 AtPath "/api/foo":
2 AtPath "/api/foo":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
5 ResponsePayload:
5 ResponsePayload:
6 PayloadSchema: UnguardedRecursion
6 PayloadSchema: UnguardedRecursion
7−backwardChanges:
7+nonBreakingChanges:
8 AtPath "/api/foo":
8 AtPath "/api/foo":
9 InOperation GetMethod:
9 InOperation GetMethod:
10 WithStatusCode 200:
10 WithStatusCode 200:
test/golden/common/variant-added/report.md+7−7
@@ -1,24 +1,24 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 1 | 1 |
5+| 1 | 1 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ⬅️☁️ JSON Response – 200
11+### 📱⬅️ JSON Response – 200
12
12
13#### `$(Object)`
13#### `$(Object)`
14
14
15The case where `$.tag` is `"C"` – has been added.
15The case where `$.tag` is `"C"` – has been added.
16
16
17−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
17+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
18
18
19## **POST** /test
19## **POST** /test
20
20
21−### ➡️☁️ JSON Request
21+### 📱➡️ JSON Request
22
22
23#### `$(Object)`
23#### `$(Object)`
24
24
test/golden/common/variant-added/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
@@ -6,7 +6,7 @@ forwardChanges:
6 PayloadSchema:
6 PayloadSchema:
7 OfType Object: PartitionBecomesEmpty (PInProperty "tag" PHere,CByEnumValue
7 OfType Object: PartitionBecomesEmpty (PInProperty "tag" PHere,CByEnumValue
8 (fromList [String "C"]))
8 (fromList [String "C"]))
9−backwardChanges:
9+nonBreakingChanges:
10 AtPath "/test":
10 AtPath "/test":
11 InOperation PostMethod:
11 InOperation PostMethod:
12 InRequest:
12 InRequest:
test/golden/common/variant-changed/report.md+9−9
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 2 | 2 |
5+| 2 | 2 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### In cases where `$.tag` is `"B"`
13#### In cases where `$.tag` is `"B"`
14
14
@@ -16,7 +16,7 @@
16
16
17The type has been removed.
17The type has been removed.
18
18
19−### ⬅️☁️ JSON Response – 200
19+### 📱⬅️ JSON Response – 200
20
20
21#### In cases where `$.tag` is `"B"`
21#### In cases where `$.tag` is `"B"`
22
22
@@ -24,11 +24,11 @@ The type has been removed.
24
24
25The type has been added.
25The type has been added.
26
26
27−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
27+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
28
28
29## **POST** /test
29## **POST** /test
30
30
31−### ➡️☁️ JSON Request
31+### 📱➡️ JSON Request
32
32
33#### In cases where `$.tag` is `"B"`
33#### In cases where `$.tag` is `"B"`
34
34
@@ -36,7 +36,7 @@ The type has been added.
36
36
37The type has been added.
37The type has been added.
38
38
39−### ⬅️☁️ JSON Response – 200
39+### 📱⬅️ JSON Response – 200
40
40
41#### In cases where `$.tag` is `"B"`
41#### In cases where `$.tag` is `"B"`
42
42
test/golden/common/variant-changed/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -15,7 +15,7 @@ forwardChanges:
15 InPartition (PInProperty "tag" PHere,CByEnumValue (fromList [String "B"])):
15 InPartition (PInProperty "tag" PHere,CByEnumValue (fromList [String "B"])):
16 InProperty "prop_B":
16 InProperty "prop_B":
17 OfType String: TypeBecomesEmpty
17 OfType String: TypeBecomesEmpty
18−backwardChanges:
18+nonBreakingChanges:
19 AtPath "/test":
19 AtPath "/test":
20 InOperation PostMethod:
20 InOperation PostMethod:
21 InRequest:
21 InRequest:
test/golden/common/variant-deep-changed/report.md+9−9
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3−| [❌ Breaking changes](#breaking-changes) | [⚠️ Non-breaking changes](#non-breaking-changes) |
3+| [⚠️ Breaking changes](#breaking-changes) | [🙆 Non-breaking changes](#non-breaking-changes) | 🤷 Unsupported feature changes |
4−|------------------------------------------|--------------------------------------------------|
4+|------------------------------------------|-------------------------------------------------|-------------------------------|
5−| 2 | 2 |
5+| 2 | 2 | 0 |
6
6
7−# <span id="breaking-changes"></span>❌ Breaking changes
7+# <span id="breaking-changes"></span>⚠️ Breaking changes
8
8
9## **POST** /test
9## **POST** /test
10
10
11−### ➡️☁️ JSON Request
11+### 📱➡️ JSON Request
12
12
13#### In cases where `$.desc.name` is `"B"`
13#### In cases where `$.desc.name` is `"B"`
14
14
@@ -16,7 +16,7 @@
16
16
17The type has been removed.
17The type has been removed.
18
18
19−### ⬅️☁️ JSON Response – 200
19+### 📱⬅️ JSON Response – 200
20
20
21#### In cases where `$.desc.name` is `"B"`
21#### In cases where `$.desc.name` is `"B"`
22
22
@@ -24,11 +24,11 @@ The type has been removed.
24
24
25The type has been added.
25The type has been added.
26
26
27−# <span id="non-breaking-changes"></span>⚠️ Non-breaking changes
27+# <span id="non-breaking-changes"></span>🙆 Non-breaking changes
28
28
29## **POST** /test
29## **POST** /test
30
30
31−### ➡️☁️ JSON Request
31+### 📱➡️ JSON Request
32
32
33#### In cases where `$.desc.name` is `"B"`
33#### In cases where `$.desc.name` is `"B"`
34
34
@@ -36,7 +36,7 @@ The type has been added.
36
36
37The type has been added.
37The type has been added.
38
38
39−### ⬅️☁️ JSON Response – 200
39+### 📱⬅️ JSON Response – 200
40
40
41#### In cases where `$.desc.name` is `"B"`
41#### In cases where `$.desc.name` is `"B"`
42
42
test/golden/common/variant-deep-changed/trace-tree.yaml+2−2
@@ -1,4 +1,4 @@
1−forwardChanges:
1+breakingChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -15,7 +15,7 @@ forwardChanges:
15 InPartition (PInProperty "desc" (PInProperty "name" PHere),CByEnumValue (fromList [String "B"])):
15 InPartition (PInProperty "desc" (PInProperty "name" PHere),CByEnumValue (fromList [String "B"])):
16 InProperty "prop_B":
16 InProperty "prop_B":
17 OfType String: TypeBecomesEmpty
17 OfType String: TypeBecomesEmpty
18−backwardChanges:
18+nonBreakingChanges:
19 AtPath "/test":
19 AtPath "/test":
20 InOperation PostMethod:
20 InOperation PostMethod:
21 InRequest:
21 InRequest: