on master help sign in
.github/actions/common_setup/action.yamlnew file+42
since splitsnapshots
unifiedsplit
@@ -0,0 +1,42 @@
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@v16
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@v10
40+ with:
41+ name: typeable
42+ authToken: "${{ inputs.authToken }}"
.github/workflows/github-actions-image.yamlnew file+311
@@ -0,0 +1,311 @@
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+ policeman:
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: Run policeman
41+ run: |
42+ jq --argjson msg "$( $(nix-build ci/policeman.nix)/bin/policeman | grep -v "โš™" | jq -aRs . )" ".[0].message = \$msg" <policeman.json >message.json
43+ cat message.json
44+
45+ - name: Post notice
46+ uses: yuzutech/annotations-action@v0.4.0
47+ with:
48+ title: ๐Ÿ‘ฎ Policeman
49+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
50+ input: "./message.json"
51+
52+ check_versions:
53+ runs-on: ubuntu-latest
54+ steps:
55+ - uses: actions/checkout@v2
56+ with:
57+ submodules: recursive
58+
59+ - name: Check library version
60+ if: github.event_name == 'release'
61+ run: |
62+ if [ "$(awk -F ':' '/^version/ {gsub(/ /, "", $2); print $2}' compaREST.cabal)" != "${{ github.event.release.tag_name }}" ]; then
63+ echo "::error file=compaREST.cabal::Library version does not match release version."
64+ exit 1
65+ fi
66+
67+ - name: Check docker tag
68+ if: github.event_name == 'release'
69+ run: |
70+ if [ "$(yq eval '.runs.image' action.yaml | awk -F ':' '{print $NF}')" != "${{ github.event.release.tag_name }}" ]; then
71+ echo "::error file=action.yaml::Action docker tag does not match release tag."
72+ exit 1
73+ fi
74+
75+ - name: Check changelog
76+ if: github.event_name == 'release'
77+ run: |
78+ if ! grep '^## ${{ github.event.release.tag_name }}$' CHANGELOG.md; then
79+ echo "::error file=CHANGELOG.md::CHANGELOG.md does not contain an entry for this release."
80+ exit 1
81+ fi
82+
83+ Linux:
84+ needs: check_versions
85+ runs-on: ubuntu-latest
86+ steps:
87+ - uses: actions/checkout@v2
88+ with:
89+ submodules: recursive
90+
91+ - name: Common setup
92+ uses: ./.github/actions/common_setup
93+ with:
94+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
95+
96+ - name: Test
97+ shell: bash
98+ run: |
99+ $(nix-build -A test -j auto --no-out-link)/bin/compaREST-tests
100+
101+ - name: Build Linux static binary
102+ shell: bash
103+ run: |
104+ cp $(nix-build -A compaRESTStaticBin -j auto --no-out-link)/bin/compaREST compaREST
105+
106+ - name: Upload compaREST Linux Binary
107+ uses: actions/upload-artifact@v2
108+ with:
109+ name: compaREST-Linux
110+ path: compaREST
111+
112+ - name: Build Docker Images
113+ shell: bash
114+ run: |
115+ cp $(nix-build -A compaRESTGithubAction -j auto --no-out-link) compaREST-Github-Action-image.tar.gz
116+ cp $(nix-build -A compaRESTImage -j auto --no-out-link) compaREST-image.tar.gz
117+
118+ - name: Upload compaREST Docker Github Action Image
119+ uses: actions/upload-artifact@v2
120+ with:
121+ name: compaREST Github Action Docker Image
122+ path: compaREST-Github-Action-image.tar.gz
123+
124+ - name: Upload compaREST Docker Image
125+ uses: actions/upload-artifact@v2
126+ with:
127+ name: compaREST Docker Image
128+ path: compaREST-image.tar.gz
129+
130+ - name: Install skopeo
131+ if: github.event_name == 'release'
132+ shell: bash
133+ run: |
134+ sudo apt-get -y update
135+ sudo apt-get -y install skopeo
136+
137+ - name: Login to DockerHub
138+ uses: docker/login-action@v1.10.0
139+ with:
140+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
141+ password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
142+
143+ - name: Push Nightly Docker Images to DockerHub
144+ if: github.ref == 'refs/heads/master' && github.event_name == 'push'
145+ shell: bash
146+ run: |
147+ skopeo copy docker-archive:$(nix-build -A compaRESTGithubAction -j auto --no-out-link) docker://ilyakooo0/comparest-github-action:nightly
148+ skopeo copy docker-archive:$(nix-build -A compaRESTImage -j auto --no-out-link) docker://ilyakooo0/comparest:nightly
149+
150+ - name: Push Release Docker Images to DockerHub
151+ if: github.event_name == 'release'
152+ shell: bash
153+ run: |
154+ skopeo copy docker-archive:$(nix-build -A compaRESTGithubAction -j auto --no-out-link) docker://ilyakooo0/comparest-github-action:${{ github.event.release.tag_name }}
155+ skopeo copy docker-archive:$(nix-build -A compaRESTImage -j auto --no-out-link) docker://ilyakooo0/comparest:${{ github.event.release.tag_name }}
156+
157+ - name: Zip Release Assets
158+ if: github.event_name == 'release'
159+ shell: bash
160+ run: |
161+ zip compaREST-Linux-x86.zip compaREST
162+
163+ - name: Upload compaREST Github Action Docker Image to release
164+ if: github.event_name == 'release'
165+ uses: svenstaro/upload-release-action@2.2.1
166+ with:
167+ repo_token: ${{ secrets.GITHUB_TOKEN }}
168+ file: compaREST-Github-Action-image.tar.gz
169+ tag: ${{ github.ref }}
170+
171+ - name: Upload compaREST Docker Image to release
172+ if: github.event_name == 'release'
173+ uses: svenstaro/upload-release-action@2.2.1
174+ with:
175+ repo_token: ${{ secrets.GITHUB_TOKEN }}
176+ file: compaREST-image.tar.gz
177+ tag: ${{ github.ref }}
178+
179+ - name: Upload compaREST binary โ€“ Linux โ€“ x86 to release
180+ if: github.event_name == 'release'
181+ uses: svenstaro/upload-release-action@2.2.1
182+ with:
183+ repo_token: ${{ secrets.GITHUB_TOKEN }}
184+ file: compaREST-Linux-x86.zip
185+ tag: ${{ github.ref }}
186+
187+ dist:
188+ needs: check_versions
189+ runs-on: ubuntu-latest
190+ steps:
191+ - uses: actions/checkout@v2
192+ with:
193+ submodules: recursive
194+
195+ - name: Cache
196+ uses: actions/cache@v1
197+ with:
198+ path: |
199+ ~/.stack
200+ .stack-work
201+ key: ${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}-${{ hashFiles('**.cabal') }}
202+ restore-keys: |
203+ ${{ runner.os }}-stack-${{ hashFiles('stack.yaml') }}
204+ ${{ runner.os }}-stack
205+
206+ - name: Upgrade stack
207+ shell: bash
208+ # Needed for Hackage token authorization
209+ run: stack upgrade --git
210+
211+ - name: Build dist
212+ run: |
213+ stack sdist --pvp-bounds both --test-tarball
214+ echo "distPath=$(stack path | awk -F ": " '/dist-dir/ {print $2}')" >> $GITHUB_ENV
215+
216+ - name: Upload compaREST sources artifact
217+ uses: actions/upload-artifact@v2
218+ with:
219+ name: compaREST sources
220+ path: ${{ env.distPath }}/*.tar.gz
221+
222+ - name: Upload compaREST sources to release
223+ if: github.event_name == 'release'
224+ uses: svenstaro/upload-release-action@2.2.1
225+ with:
226+ repo_token: ${{ secrets.GITHUB_TOKEN }}
227+ file_glob: true
228+ file: ${{ env.distPath }}/*.tar.gz
229+ tag: ${{ github.ref }}
230+
231+ - name: Publish compaREST candidate to Hackage
232+ shell: bash
233+ if: github.event_name == 'release'
234+ env:
235+ HACKAGE_KEY: ${{ secrets.HACKAGE_KEY }}
236+ run: |
237+ stack upload --pvp-bounds both --test-tarball --candidate .
238+
239+ macOS:
240+ needs: check_versions
241+ runs-on: macos-latest
242+ steps:
243+ - uses: actions/checkout@v2
244+ with:
245+ submodules: recursive
246+
247+ - name: Common setup
248+ uses: ./.github/actions/common_setup
249+ with:
250+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
251+
252+ - name: Build macOS binary bundle
253+ run: |
254+ cp -LR $(nix-build -A macOSCompaRESTBundle -j auto --no-out-link) compaREST
255+
256+ - name: Upload macOS compaREST bundle
257+ uses: actions/upload-artifact@v2
258+ with:
259+ name: compaREST binary โ€“ macOS โ€“ x86
260+ path: compaREST
261+
262+ - name: Zip Release Assets
263+ if: github.event_name == 'release'
264+ shell: bash
265+ run: |
266+ zip -r compaREST-macOS-x86.zip compaREST
267+
268+ - name: Upload compaREST binary โ€“ macOS โ€“ x86 to release
269+ if: github.event_name == 'release'
270+ uses: svenstaro/upload-release-action@2.2.1
271+ with:
272+ repo_token: ${{ secrets.GITHUB_TOKEN }}
273+ file: compaREST-macOS-x86.zip
274+ tag: ${{ github.ref }}
275+
276+ Linux-Windows:
277+ needs: check_versions
278+ runs-on: ubuntu-latest
279+ steps:
280+ - uses: actions/checkout@v2
281+ with:
282+ submodules: recursive
283+
284+ - name: Common setup
285+ uses: ./.github/actions/common_setup
286+ with:
287+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
288+
289+ - name: Build Windows binary
290+ run: |
291+ cp -LR $(nix-build -A WindowsCompaRESTBin -j auto --no-out-link)/bin compaREST
292+
293+ - name: Upload Windows compaREST bundle
294+ uses: actions/upload-artifact@v2
295+ with:
296+ name: compaREST binary โ€“ Windows
297+ path: compaREST
298+
299+ - name: Zip Release Assets
300+ if: github.event_name == 'release'
301+ shell: bash
302+ run: |
303+ zip -r compaREST-Windows.zip compaREST
304+
305+ - name: Upload compaREST binary โ€“ Windows to release
306+ if: github.event_name == 'release'
307+ uses: svenstaro/upload-release-action@2.2.1
308+ with:
309+ repo_token: ${{ secrets.GITHUB_TOKEN }}
310+ file: compaREST-Windows.zip
311+ tag: ${{ github.ref }}
.github/workflows/haskell.ymldeletedโˆ’54
@@ -1,54 +0,0 @@
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.yamldeletedโˆ’17
@@ -1,17 +0,0 @@
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
CHANGELOG.md+1โˆ’1
@@ -1,6 +1,6 @@
1# Changelog
1# Changelog
2
2
3โˆ’`openapi-diff` uses [PVP Versioning][1].
3+`comparest` uses [PVP Versioning][1].
4
4
5## 0.0.0.0
5## 0.0.0.0
6
6
README.md+245โˆ’13
@@ -1,40 +1,272 @@
1โˆ’# openapi-diff
1+# CompaREST
2
2
3โˆ’[![Hackage](https://img.shields.io/hackage/v/openapi-diff.svg?logo=haskell)](https://hackage.haskell.org/package/openapi-diff)
3+[![Hackage](https://img.shields.io/hackage/v/comparest.svg?logo=haskell)](https://hackage.haskell.org/package/comparest)
4โˆ’[![Stackage Lts](http://stackage.org/package/openapi-diff/badge/lts)](http://stackage.org/lts/package/openapi-diff)
4+[![Stackage Lts](http://stackage.org/package/comparest/badge/lts)](http://stackage.org/lts/package/comparest)
5โˆ’[![Stackage Nightly](http://stackage.org/package/openapi-diff/badge/nightly)](http://stackage.org/nightly/package/openapi-diff)
5+[![Stackage Nightly](http://stackage.org/package/comparest/badge/nightly)](http://stackage.org/nightly/package/comparest)
6[![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)
7
7
8Compatibility checker for OpenAPI
8Compatibility checker for OpenAPI
9+# Quick Start Guide
9
10
11+## Your situation
12+
13+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.)
14+
15+You have recently released version 1.0 and things are going great: user are downloading your app, servers are processing requests.
16+
17+You describe your API in a file `api-1.0.0.yaml`:
18+
19+```yaml
20+openapi: "3.0.0"
21+info:
22+ version: 1.0.0
23+ title: Swagger Petstore
24+ license:
25+ name: MIT
26+servers:
27+ - url: https://example.com
28+paths:
29+ /pets:
30+ get:
31+ parameters:
32+ - name: limit
33+ in: query
34+ required: false
35+ schema:
36+ type: integer
37+ maximum: 20
38+ responses:
39+ "200":
40+ description: ""
41+ headers:
42+ x-next:
43+ schema:
44+ type: string
45+ content:
46+ application/json:
47+ schema:
48+ $ref: "#/components/schemas/Pets"
49+ post:
50+ requestBody:
51+ content:
52+ application/json:
53+ schema:
54+ $ref: "#/components/schemas/Pet"
55+ responses:
56+ "201":
57+ description: ""
58+components:
59+ schemas:
60+ Pet:
61+ type: object
62+ required:
63+ - id
64+ - name
65+ properties:
66+ id:
67+ type: integer
68+ name:
69+ type: string
70+ minLength: 3
71+ maxLength: 10
72+ Pets:
73+ type: array
74+ items:
75+ $ref: "#/components/schemas/Pet"
76+```
77+
78+## Evolving your product
79+
80+Enthused over your initial success you hurry to release a new and improved version of your API and mobile app.
81+
82+After a round of very intense programming you take a look at your new `api-1.1.0.yaml`:
83+
84+```yaml
85+openapi: "3.0.0"
86+info:
87+ version: 1.1.0
88+ title: Swagger Petstore
89+ license:
90+ name: MIT
91+servers:
92+ - url: https://example.com
93+paths:
94+ /pets:
95+ get:
96+ parameters:
97+ - name: limit
98+ in: query
99+ required: false
100+ schema:
101+ type: integer
102+ maximum: 30
103+ responses:
104+ "200":
105+ description: ""
106+ headers:
107+ x-next:
108+ schema:
109+ type: string
110+ content:
111+ application/json:
112+ schema:
113+ $ref: "#/components/schemas/Pets"
114+ post:
115+ requestBody:
116+ content:
117+ application/json:
118+ schema:
119+ $ref: "#/components/schemas/Pet"
120+ responses:
121+ "201":
122+ description: ""
123+components:
124+ schemas:
125+ Pet:
126+ type: object
127+ required:
128+ - id
129+ - name
130+ properties:
131+ id:
132+ type: integer
133+ name:
134+ type: string
135+ minLength: 1
136+ maxLength: 15
137+ weight:
138+ type: integer
139+ Pets:
140+ type: array
141+ items:
142+ $ref: "#/components/schemas/Pet"
143+```
144+
145+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.
146+
147+## Assessing compatibility automatically
148+
149+Luckily, you have access to compaREST which can programmatically analyze your APIs and determine what, if anything, breaks compatibility and what doesn't.
150+
151+You can call it, passing the API your client will be aware of, and the API your server will serve like so:
152+
153+```bash
154+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
155+```
156+
157+Running this command will output a file `report.md`, containing the compatibility report between the two APIs:
158+
159+> # Summary
160+>
161+> | [โš ๏ธ Breaking changes](#breaking-changes) | [๐Ÿ™† Non-breaking changes](#non-breaking-changes) | ๐Ÿคท Unsupported feature changes |
162+> |------------------------------------------|-------------------------------------------------|-------------------------------|
163+> | 5 | 6 | 0 |
164+>
165+> # <span id="breaking-changes"></span>โš ๏ธ Breaking changes
166+>
167+> ## **GET** /pets
168+>
169+> ### ๐Ÿ“ฑโฌ…๏ธ JSON Response โ€“ 200
170+>
171+> #### `$[*].name(String)`
172+>
173+> 1. Maximum length of the string changed from 10 to 15.
174+>
175+> 2. Minimum length of the string changed from 3 to 1.
176+>
177+> ## **POST** /pets
178+>
179+> ### ๐Ÿ“ฑโžก๏ธ JSON Request
180+>
181+> #### `$.weight`
182+>
183+> 1. Values are now limited to the following types:
184+>
185+> - Number
186+>
187+> 2. The property was previously implicitly described by the catch-all
188+> "additional properties" case. It is now explicitly defined.
189+>
190+> #### `$.weight(Number)`
191+>
192+> Value is now a multiple of 1.0.
193+>
194+> # <span id="non-breaking-changes"></span>๐Ÿ™† Non-breaking changes
195+>
196+> ## **GET** /pets
197+>
198+> ### Parameter limit
199+>
200+> #### JSON Schema
201+>
202+> ##### `$(Number)`
203+>
204+> Upper bound changed from 20.0 inclusive to 30.0 inclusive.
205+>
206+> ### ๐Ÿ“ฑโฌ…๏ธ JSON Response โ€“ 200
207+>
208+> #### `$[*].weight`
209+>
210+> 1. Values are now limited to the following types:
211+>
212+> - Number
213+>
214+> 2. The property was previously implicitly described by the catch-all
215+> "additional properties" case. It is now explicitly defined.
216+>
217+> #### `$[*].weight(Number)`
218+>
219+> Value is now a multiple of 1.0.
220+>
221+> ## **POST** /pets
222+>
223+> ### ๐Ÿ“ฑโžก๏ธ JSON Request
224+>
225+> #### `$.name(String)`
226+>
227+> 1. Maximum length of the string changed from 10 to 15.
228+>
229+> 2. Minimum length of the string changed from 3 to 1.
230+
231+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.
232+
233+## Additional formats
234+
235+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:
236+
237+```bash
238+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
239+```
240+
241+# CLI docs
10
242
11```
243```
12โˆ’Usage: openapi-diff (-c|--client ARG) (-s|--server ARG)
244+Usage: comparest (-c|--client ARG) (-s|--server ARG)
13โˆ’ [--silent | --only-breaking | --all] [-o|--output ARG]
245+ [--silent | --only-breaking | --all] [-o|--output ARG]
14โˆ’ [--folding-block-quotes-style | --header-style]
246+ [--folding-block-quotes-style | --header-style]
15โˆ’ [--signal-exit-code]
247+ [--signal-exit-code]
16โˆ’ A tool to check compatibility between two OpenApi specifications.
248+ A tool to check compatibility between two OpenAPI specifications.
17
249
18 Usage examples
250 Usage examples
19
251
20 Compare files old.yaml with new.yaml and output the resulting report to
252 Compare files old.yaml with new.yaml and output the resulting report to
21 stdout:
253 stdout:
22
254
23โˆ’ openapi-diff -c old.yaml -s new.yaml
255+ comparest -c old.yaml -s new.yaml
24
256
25 Only output breaking changes and write a styled HTML report to file
257 Only output breaking changes and write a styled HTML report to file
26 report.html:
258 report.html:
27
259
28โˆ’ openapi-diff -c old.yaml -s new.yaml --only-breaking -o report
260+ comparest -c old.yaml -s new.yaml --only-breaking -o report
29
261
30 Don't output anything, only fail if there are breaking changes:
262 Don't output anything, only fail if there are breaking changes:
31
263
32โˆ’ openapi-diff -c old.json -s new.json --silent
264+ comparest -c old.json -s new.json --silent
33
265
34 Write full report suitable for embedding into a GitHub comment to
266 Write full report suitable for embedding into a GitHub comment to
35 report.html:
267 report.html:
36
268
37โˆ’ openapi-diff -c old.json -s new.json --folding-block-quotes-style -o report.html
269+ comparest -c old.json -s new.json --folding-block-quotes-style -o report.html
38
270
39Available options:
271Available options:
40 -h,--help Show this help text
272 -h,--help Show this help text
action.yamlnew file+47
@@ -0,0 +1,47 @@
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 comment.
11+ default: "${{ github.repository }}"
12+ required: false
13+ project_name:
14+ description: The name of the project to which the API pertains.
15+ required: true
16+ footer:
17+ description: A footer that can be appended to the comment.
18+ required: false
19+ default: ""
20+ old:
21+ description: The path to old specification of the API.
22+ required: true
23+ new:
24+ description: The path to new specification of the API.
25+ required: true
26+ sha:
27+ description: The sha of the commit to post the check for.
28+ required: false
29+ default: "${{ github.event.pull_request.head.sha }}"
30+runs:
31+ using: "docker"
32+ image: "docker://typeable/comparest-github-action:0.0.0.0"
33+ env:
34+ GITHUB_TOKEN: "${{ inputs.GITHUB_TOKEN }}"
35+ REPO: "${{ inputs.repo }}"
36+ PROJECT_NAME: "${{ inputs.project_name }}"
37+ FOOTER: "${{ inputs.footer }}"
38+ ROOT: "/github/workspace"
39+ OLD: "${{ inputs.old }}"
40+ NEW: "${{ inputs.new }}"
41+ SHA: "${{ inputs.sha }}"
42+ pre-entrypoint: "/bin/pre"
43+ entrypoint: "/bin/run"
44+
45+branding:
46+ icon: crosshair
47+ color: gray-dark
app/Data/OpenApi/Compare/Options.hsnew file+134
@@ -0,0 +1,134 @@
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+
13+parseOptions :: IO Options
14+parseOptions = customExecParser (prefs $ showHelpOnError) optionsParserInfo
15+
16+data Options = Options
17+ { clientFile :: FilePath
18+ , serverFile :: FilePath
19+ , -- | 'Nothing' means "don't produce any output"
20+ mode :: Maybe ReportMode
21+ , outputMode :: OutputMode
22+ , reportTreeStyle :: ReportTreeStyle
23+ , signalExitCode :: Bool
24+ }
25+ deriving stock (Generic)
26+
27+data OutputMode = StdoutMode | FileMode FilePath
28+
29+optionsParserInfo :: ParserInfo Options
30+optionsParserInfo =
31+ info
32+ (helper <*> optionsParser)
33+ ( fullDesc
34+ <> header "CompaREST"
35+ <> progDescDoc
36+ ( Just $
37+ par "A tool to check compatibility between two OpenAPI specifications."
38+ <$$> hardline <> par "Usage examples" <> hardline
39+ <$$> indent
40+ 4
41+ ( par "Compare files old.yaml with new.yaml and output the resulting report to stdout:"
42+ <$$> hardline <> indent 4 "comparest -c old.yaml -s new.yaml"
43+ <$$> hardline <> par "Only output breaking changes and write a styled HTML report to file report.html:"
44+ <$$> hardline <> indent 4 "comparest -c old.yaml -s new.yaml --only-breaking -o report"
45+ <$$> hardline <> par "Don't output anything, only fail if there are breaking changes:"
46+ <$$> hardline <> indent 4 "comparest -c old.json -s new.json --silent"
47+ <$$> hardline <> par "Write full report suitable for embedding into a GitHub comment to report.html:"
48+ <$$> hardline <> indent 4 "comparest -c old.json -s new.json --folding-block-quotes-style -o report.html"
49+ )
50+ )
51+ )
52+
53+optionsParser :: Parser Options
54+optionsParser =
55+ Options
56+ <$> strOption
57+ ( short 'c'
58+ <> long "client"
59+ <> help
60+ "A path to the file containing the specification that will be \
61+ \used for the client of the API. Can be either a YAML or JSON file."
62+ )
63+ <*> strOption
64+ ( short 's'
65+ <> long "server"
66+ <> help
67+ "A path to the file containing the specification that will be \
68+ \used for the server of the API. Can be either a YAML or JSON file."
69+ )
70+ <*> ( flag'
71+ Nothing
72+ ( long "silent"
73+ <> help "Silence all output. Only makes sense in combination with --signal-exit-code."
74+ )
75+ <|> flag'
76+ (Just OnlyErrors)
77+ ( long "only-breaking"
78+ <> help "Only report breaking changes in the output."
79+ )
80+ <|> flag'
81+ (Just All)
82+ ( long "all"
83+ <> help
84+ "Report both incompatible and compatible changes. \
85+ \Compatible changes will not trigger a failure exit code."
86+ )
87+ <|> pure (Just All)
88+ )
89+ <*> ( ( FileMode
90+ <$> strOption
91+ ( short 'o' <> long "output"
92+ <> helpDoc
93+ ( Just $
94+ par "The file path where the output should be written. If the option is omitted the result will be written to stdout."
95+ <$$> hardline <> par "The file extension is used to determine the type of the output file."
96+ <$$> hardline <> par "Supports many formats such as markdown, html, rtf, doc, txt, rst, and many more."
97+ <$$> hardline <> par "Leave out the extension to produce a self-contained HTML report with styling."
98+ )
99+ )
100+ )
101+ <|> pure StdoutMode
102+ )
103+ <*> ( flag'
104+ FoldingBlockquotesTreeStyle
105+ ( long "folding-block-quotes-style"
106+ <> help
107+ "The report tree is structured using \
108+ \summary/detail HTML elements and indented using \
109+ \block quotes. This style renders well on GitHub.\
110+ \Intended for HTML output format. Markdown has rendering \
111+ \bugs on GitHub."
112+ )
113+ <|> flag'
114+ HeadersTreeStyle
115+ ( long "header-style"
116+ <> help
117+ "The report tree is structured using \
118+ \increasing levels of headers."
119+ )
120+ <|> pure HeadersTreeStyle
121+ )
122+ <*> switch
123+ ( long "signal-exit-code"
124+ <> helpDoc
125+ ( Just $
126+ par "Signal API compatibility with the exit code."
127+ <$$> hardline <> par "Exit with 0 if there are no breaking changes."
128+ <$$> par "Exit with 1 if there are breaking changes."
129+ <$$> par "Exit with 2 if could not determine compatibility."
130+ )
131+ )
132+
133+par :: String -> Doc
134+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
13import Data.Text (Text)
14import Data.Text (Text)
14import qualified Data.Text.Lazy as TL
15import qualified Data.Text.Lazy as TL
15import qualified Data.Text.Lazy.Encoding as TL
16import 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 ("OpenApi Diff" :: Text))]
83+ writerVariables = Context $ M.fromList [("pagetitle", toVal ("CompaREST" :: Text))]
84 }
84 }
85 _ -> Nothing
85 _ -> Nothing
86 where
86 where
app/Main.hs+4โˆ’3
@@ -6,14 +6,15 @@ import Data.Aeson
6import qualified Data.ByteString.Lazy as BSL
6import qualified Data.ByteString.Lazy as BSL
7import Data.Default
7import Data.Default
8import Data.Maybe
8import Data.Maybe
9+import Data.OpenApi.Compare.Options
10+import Data.OpenApi.Compare.Run
9import qualified Data.Text.IO as T
11import qualified Data.Text.IO as T
10import qualified Data.Yaml as Yaml
12import qualified Data.Yaml as Yaml
11import FormatHeuristic
13import FormatHeuristic
12โˆ’import OpenAPI.Checker.Options
13โˆ’import OpenAPI.Checker.Run
14import System.Exit
14import System.Exit
15import System.IO
15import System.IO
16import Text.Pandoc hiding (report)
16import Text.Pandoc hiding (report)
17+import Text.Pandoc.Builder
17
18
18main :: IO ()
19main :: IO ()
19main = do
20main = do
@@ -47,7 +48,7 @@ main = do
47 }
48 }
48 (report, status) = runReport reportConfig (a, b)
49 (report, status) = runReport reportConfig (a, b)
49 case mode opts of
50 case mode opts of
50โˆ’ Just _ -> either handler pure <=< runExceptT $ write report
51+ Just _ -> either handler pure <=< runExceptT $ write $ doc report
51 Nothing -> pure ()
52 Nothing -> pure ()
52 when (signalExitCode opts) $
53 when (signalExitCode opts) $
53 case status of
54 case status of
app/OpenAPI/Checker/Options.hsdeletedโˆ’115
@@ -1,115 +0,0 @@
1โˆ’module OpenAPI.Checker.Options
2โˆ’ ( Options (..)
3โˆ’ , OutputMode (..)
4โˆ’ , parseOptions
5โˆ’ )
6โˆ’where
7โˆ’
8โˆ’import GHC.Generics (Generic)
9โˆ’import OpenAPI.Checker.Report
10โˆ’import Options.Applicative
11โˆ’import Options.Applicative.Help hiding (fullDesc)
12โˆ’
13โˆ’parseOptions :: IO Options
14โˆ’parseOptions = customExecParser (prefs $ showHelpOnError) optionsParserInfo
15โˆ’
16โˆ’data Options = Options
17โˆ’ { clientFile :: FilePath
18โˆ’ , serverFile :: FilePath
19โˆ’ , -- | 'Nothing' means "don't produce any output"
20โˆ’ mode :: Maybe ReportMode
21โˆ’ , outputMode :: OutputMode
22โˆ’ , reportTreeStyle :: ReportTreeStyle
23โˆ’ , signalExitCode :: Bool
24โˆ’ }
25โˆ’ deriving stock (Generic)
26โˆ’
27โˆ’data OutputMode = StdoutMode | FileMode FilePath
28โˆ’
29โˆ’optionsParserInfo :: ParserInfo Options
30โˆ’optionsParserInfo =
31โˆ’ info
32โˆ’ (helper <*> optionsParser)
33โˆ’ (fullDesc
34โˆ’ <> header "openapi-diff"
35โˆ’ <> progDescDoc
36โˆ’ (Just $
37โˆ’ par "A tool to check compatibility between two OpenApi specifications."
38โˆ’ <$$> hardline <> par "Usage examples" <> hardline
39โˆ’ <$$> indent
40โˆ’ 4
41โˆ’ (par "Compare files old.yaml with new.yaml and output the resulting report to stdout:"
42โˆ’ <$$> hardline <> indent 4 "openapi-diff -c old.yaml -s new.yaml"
43โˆ’ <$$> hardline <> par "Only output breaking changes and write a styled HTML report to file report.html:"
44โˆ’ <$$> hardline <> indent 4 "openapi-diff -c old.yaml -s new.yaml --only-breaking -o report"
45โˆ’ <$$> hardline <> par "Don't output anything, only fail if there are breaking changes:"
46โˆ’ <$$> hardline <> indent 4 "openapi-diff -c old.json -s new.json --silent"
47โˆ’ <$$> hardline <> par "Write full report suitable for embedding into a GitHub comment to report.html:"
48โˆ’ <$$> hardline <> indent 4 "openapi-diff -c old.json -s new.json --folding-block-quotes-style -o report.html")))
49โˆ’
50โˆ’optionsParser :: Parser Options
51โˆ’optionsParser =
52โˆ’ Options
53โˆ’ <$> strOption
54โˆ’ (short 'c'
55โˆ’ <> long "client"
56โˆ’ <> help
57โˆ’ "A path to the file containing the specification that will be \
58โˆ’ \used for the client of the API. Can be either a YAML or JSON file.")
59โˆ’ <*> strOption
60โˆ’ (short 's'
61โˆ’ <> long "server"
62โˆ’ <> help
63โˆ’ "A path to the file containing the specification that will be \
64โˆ’ \used for the server of the API. Can be either a YAML or JSON file.")
65โˆ’ <*> (flag'
66โˆ’ Nothing
67โˆ’ (long "silent"
68โˆ’ <> help "Silence all output. Only makes sense in combination with --signal-exit-code.")
69โˆ’ <|> flag'
70โˆ’ (Just OnlyErrors)
71โˆ’ (long "only-breaking"
72โˆ’ <> help "Only report breaking changes in the output.")
73โˆ’ <|> flag'
74โˆ’ (Just All)
75โˆ’ (long "all"
76โˆ’ <> help
77โˆ’ "Report both incompatible and compatible changes. \
78โˆ’ \Compatible changes will not trigger a failure exit code.")
79โˆ’ <|> pure (Just All))
80โˆ’ <*> ((FileMode
81โˆ’ <$> strOption
82โˆ’ (short 'o' <> long "output"
83โˆ’ <> helpDoc
84โˆ’ (Just $
85โˆ’ par "The file path where the output should be written. If the option is omitted the result will be written to stdout."
86โˆ’ <$$> hardline <> par "The file extension is used to determine the type of the output file."
87โˆ’ <$$> hardline <> par "Supports many formats such as markdown, html, rtf, doc, txt, rst, and many more."
88โˆ’ <$$> hardline <> par "Leave out the extension to produce a self-contained HTML report with styling.")))
89โˆ’ <|> pure StdoutMode)
90โˆ’ <*> (flag'
91โˆ’ FoldingBlockquotesTreeStyle
92โˆ’ (long "folding-block-quotes-style"
93โˆ’ <> help
94โˆ’ "The report tree is structured using \
95โˆ’ \summary/detail HTML elements and indented using \
96โˆ’ \block quotes. This style renders well on GitHub.\
97โˆ’ \Intended for HTML output format. Markdown has rendering \
98โˆ’ \bugs on GitHub.")
99โˆ’ <|> flag'
100โˆ’ HeadersTreeStyle
101โˆ’ (long "header-style"
102โˆ’ <> help
103โˆ’ "The report tree is structured using \
104โˆ’ \increasing levels of headers.")
105โˆ’ <|> pure HeadersTreeStyle)
106โˆ’ <*> switch
107โˆ’ (long "signal-exit-code"
108โˆ’ <> helpDoc (Just $
109โˆ’ par "Signal API compatibility with the exit code."
110โˆ’ <$$> hardline <> par "Exit with 0 if there are no breaking changes."
111โˆ’ <$$> par "Exit with 1 if there are breaking changes."
112โˆ’ <$$> par "Exit with 2 if could not determine compatibility."))
113โˆ’
114โˆ’par :: String -> Doc
115โˆ’par = foldr1 (</>) . fmap string . words
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 OpenApi Diff](https://github.com/typeable/openapi-diff) | `typeable.md` |
15+| [Typeable CompaREST](https://github.com/typeable/compaREST) | `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+8โˆ’12
@@ -1,6 +1,6 @@
1{ sources ? import ./nix/sources.nix
1{ sources ? import ./nix/sources.nix
2โˆ’, haskellNix ? import sources.haskellNix { }
3, gomod2nix ? sources.gomod2nix
2, 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,23 +8,19 @@
8 })
8 })
9 ];
9 ];
10 })
10 })
11+, compaRESThsPkgs ? (import ../. {}).hsPkgs
11, npmNix ? import (sources.npmNix + "/npmPackages") { inherit pkgs; }
12, npmNix ? import (sources.npmNix + "/npmPackages") { inherit pkgs; }
12, mavenix ? import (sources.mavenix) { inherit pkgs; }
13, mavenix ? import (sources.mavenix) { inherit pkgs; }
13, bumpToken ? null
14, bumpToken ? null
14, bumpDocumentation ? null
15, bumpDocumentation ? null
15}:
16}:
16let
17let
17โˆ’ typeable-openapi-diff = (pkgs.haskell-nix.stackProject {
18+ typeable-comparest = compaRESThsPkgs.comparest.components.exes.comparest;
18โˆ’ src = pkgs.haskell-nix.haskellLib.cleanGit {
19โˆ’ name = "openapi-diff";
20โˆ’ src = ./..;
21โˆ’ };
22โˆ’ }).openapi-diff.components.exes.openapi-diff;
23โˆ’ typeable-openapi-diff-exe = typeable-openapi-diff + "/bin/openapi-diff";
19+ typeable-comparest-exe = typeable-comparest + "/bin/comparest";
24โˆ’ typeable-openapi-diff-differ = pkgs.writeScript "typeable-openapi-diff-differ" ''
20+ typeable-comparest-differ = pkgs.writeScript "typeable-comparest-differ" ''
25 #!${pkgs.stdenv.shell}
21 #!${pkgs.stdenv.shell}
26 set +e
22 set +e
27โˆ’ ${typeable-openapi-diff-exe} -c $1 -s $2 -o $3/typeable.md
23+ ${typeable-comparest-exe} -c $1 -s $2 -o $3/typeable.md
28 exit 0
24 exit 0
29 '';
25 '';
30
26
@@ -130,7 +126,7 @@ let
130 })
126 })
131 (getInputs root);
127 (getInputs root);
132 differs = [
128 differs = [
133โˆ’ typeable-openapi-diff-differ
129+ typeable-comparest-differ
134 atlassian-openapi-diff-differ
130 atlassian-openapi-diff-differ
135 openapitools-openapi-diff-differ
131 openapitools-openapi-diff-differ
136 oasdiff-differ
132 oasdiff-differ
@@ -164,7 +160,7 @@ let
164 tools = [
160 tools = [
165 "Typeable"
161 "Typeable"
166 "Atlassian"
162 "Atlassian"
167โˆ’ "OpenApi Tools"
163+ "OpenAPI Tools"
168 "oasdiff"
164 "oasdiff"
169 "Bump.sh"
165 "Bump.sh"
170 ];
166 ];
ci/policeman.nixnew file+4
@@ -0,0 +1,4 @@
1+{ sources ? import ../nix/sources.nix
2+, pkgs ? import sources.nixpkgs { }
3+}:
4+pkgs.haskellPackages.policeman
openapi-diff.cabal โ†’ compaREST.cabalrenamed+83โˆ’56
@@ -1,29 +1,24 @@
1cabal-version: 2.4
1cabal-version: 2.4
2โˆ’name: openapi-diff
2+name: compaREST
3version: 0.0.0.0
3version: 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: Aleksey Uymanov
8+author: Typeable
9โˆ’maintainer: Aleksey Uymanov <s9gf4ult@gmail.com>
9+maintainer: compaREST@typeable.io
10โˆ’copyright: 2021 Aleksey Uymanov
10+copyright: 2021 Typeable
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
15tested-with: GHC == 8.10.4
15tested-with: GHC == 8.10.4
16extra-source-files: awsm-css/dist/awsm.min.css
16extra-source-files: awsm-css/dist/awsm.min.css
17โˆ’
17+data-files:
18โˆ’Flag static
18+ test/golden/**/*.yaml
19โˆ’ Default: False
19+ test/golden/**/*.md
20โˆ’ Manual: True
21
20
22common common-options
21common common-options
23โˆ’ if flag(static)
24โˆ’ cc-options: -static
25โˆ’ ld-options: -static -pthread
26โˆ’ ghc-options: -static
27 ghc-options:
22 ghc-options:
28 -Weverything
23 -Weverything
29 -Wno-implicit-prelude
24 -Wno-implicit-prelude
@@ -38,9 +33,9 @@ common common-options
38 -fconstraint-solver-iterations=0
33 -fconstraint-solver-iterations=0
39 default-language: Haskell2010
34 default-language: Haskell2010
40 build-depends: base >= 4.12.0.0 && < 4.16
35 build-depends: base >= 4.12.0.0 && < 4.16
41โˆ’ , bytestring
42 , text
36 , text
43
37
38+
44 default-extensions: ApplicativeDo
39 default-extensions: ApplicativeDo
45 , BangPatterns
40 , BangPatterns
46 , ConstraintKinds
41 , ConstraintKinds
@@ -82,6 +77,7 @@ common common-options
82 , QuantifiedConstraints
77 , QuantifiedConstraints
83 , DefaultSignatures
78 , DefaultSignatures
84
79
80+
85library
81library
86 import: common-options
82 import: common-options
87 build-depends:
83 build-depends:
@@ -111,52 +107,53 @@ library
111 , file-embed
107 , file-embed
112 , data-default
108 , data-default
113 , ordered-containers
109 , ordered-containers
110+ , bytestring
114 hs-source-dirs: src
111 hs-source-dirs: src
115โˆ’ exposed-modules: Data.HList
112+ exposed-modules: Data.OpenApi.Compare.Behavior
116โˆ’ , OpenAPI.Checker.Behavior
113+ , Data.OpenApi.Compare.Common
117โˆ’ , OpenAPI.Checker.Formula
114+ , Data.OpenApi.Compare.Formula
118โˆ’ , OpenAPI.Checker.Memo
115+ , Data.OpenApi.Compare.Memo
119โˆ’ , OpenAPI.Checker.Orphans
116+ , Data.OpenApi.Compare.Orphans
120โˆ’ , OpenAPI.Checker.Paths
117+ , Data.OpenApi.Compare.Paths
121โˆ’ , OpenAPI.Checker.PathsPrefixTree
118+ , Data.OpenApi.Compare.PathsPrefixTree
122โˆ’ , OpenAPI.Checker.References
119+ , Data.OpenApi.Compare.References
123โˆ’ , OpenAPI.Checker.Subtree
120+ , Data.OpenApi.Compare.Report
121+ , Data.OpenApi.Compare.Report.Html.Template
122+ , Data.OpenApi.Compare.Report.Jet
123+ , Data.OpenApi.Compare.Run
124+ , Data.OpenApi.Compare.Subtree
125+ , Data.OpenApi.Compare.Validate.Header
126+ , Data.OpenApi.Compare.Validate.Link
124โˆ’ , OpenAPI.Checker.Validate.MediaTypeObject
127+ , Data.OpenApi.Compare.Validate.MediaTypeObject
128+ , Data.OpenApi.Compare.Validate.OAuth2Flows
125โˆ’ , OpenAPI.Checker.Validate.OpenApi
129+ , Data.OpenApi.Compare.Validate.OpenApi
126โˆ’ , OpenAPI.Checker.Validate.Operation
130+ , Data.OpenApi.Compare.Validate.Operation
127โˆ’ , OpenAPI.Checker.Validate.Param
131+ , Data.OpenApi.Compare.Validate.Param
128โˆ’ , OpenAPI.Checker.Validate.PathFragment
132+ , Data.OpenApi.Compare.Validate.PathFragment
129โˆ’ , OpenAPI.Checker.Validate.Products
133+ , Data.OpenApi.Compare.Validate.Products
130โˆ’ , OpenAPI.Checker.Validate.RequestBody
134+ , Data.OpenApi.Compare.Validate.RequestBody
131โˆ’ , OpenAPI.Checker.Validate.Responses
135+ , Data.OpenApi.Compare.Validate.Responses
132โˆ’ , OpenAPI.Checker.Validate.Schema
133โˆ’ , OpenAPI.Checker.Validate.Schema.TypedJson
136+ , Data.OpenApi.Compare.Validate.Schema
134โˆ’ , OpenAPI.Checker.Validate.Schema.DNF
137+ , Data.OpenApi.Compare.Validate.Schema.DNF
135โˆ’ , OpenAPI.Checker.Validate.Schema.Issues
138+ , Data.OpenApi.Compare.Validate.Schema.Issues
136โˆ’ , OpenAPI.Checker.Validate.Schema.JsonFormula
139+ , Data.OpenApi.Compare.Validate.Schema.JsonFormula
137โˆ’ , OpenAPI.Checker.Validate.Schema.Partition
140+ , Data.OpenApi.Compare.Validate.Schema.Partition
138โˆ’ , OpenAPI.Checker.Validate.Schema.Process
141+ , Data.OpenApi.Compare.Validate.Schema.Process
139โˆ’ , OpenAPI.Checker.Validate.Schema.Traced
142+ , Data.OpenApi.Compare.Validate.Schema.Traced
143+ , Data.OpenApi.Compare.Validate.Schema.TypedJson
140โˆ’ , OpenAPI.Checker.Validate.SecurityRequirement
144+ , Data.OpenApi.Compare.Validate.SecurityRequirement
145+ , Data.OpenApi.Compare.Validate.SecurityScheme
141โˆ’ , OpenAPI.Checker.Validate.Server
146+ , Data.OpenApi.Compare.Validate.Server
142โˆ’ , OpenAPI.Checker.Validate.Sums
147+ , Data.OpenApi.Compare.Validate.Sums
143โˆ’ , OpenAPI.Checker.Validate.Header
148+ other-modules: Data.HList
144โˆ’ , OpenAPI.Checker.Validate.Link
145โˆ’ , OpenAPI.Checker.Common
146โˆ’ , OpenAPI.Checker.Validate.SecurityScheme
147โˆ’ , OpenAPI.Checker.Validate.OAuth2Flows
148โˆ’ , OpenAPI.Checker.Report
149 , Data.OpenUnion.Extra
149 , Data.OpenUnion.Extra
150โˆ’ , OpenAPI.Checker.Report.Jet
151โˆ’ , OpenAPI.Checker.Run
152โˆ’ , OpenAPI.Checker.Report.Html.Template
153
150
154โˆ’executable openapi-diff
151+executable compaREST
155 import: common-options
152 import: common-options
156 hs-source-dirs: app
153 hs-source-dirs: app
157 main-is: Main.hs
154 main-is: Main.hs
158 build-depends:
155 build-depends:
159โˆ’ openapi-diff
156+ compaREST
160 , pandoc
157 , pandoc
161 , data-default
158 , data-default
162 , bytestring
159 , bytestring
@@ -167,14 +164,41 @@ executable openapi-diff
167 , aeson
164 , aeson
168 , containers
165 , containers
169 , doctemplates
166 , doctemplates
167+ , pandoc-types
170 ghc-options: -threaded
168 ghc-options: -threaded
171 -rtsopts
169 -rtsopts
172 -with-rtsopts=-N
170 -with-rtsopts=-N
173 other-modules:
171 other-modules:
174 FormatHeuristic
172 FormatHeuristic
175โˆ’ OpenAPI.Checker.Options
173+ Data.OpenApi.Compare.Options
174+
175+executable compaREST-GitHub-Action
176+ import: common-options
177+ hs-source-dirs: github-action
178+ main-is: Main.hs
179+ build-depends:
180+ compaREST
181+ , pandoc
182+ , yaml
183+ , aeson
184+ , github
185+ , freer-simple
186+ , vector
187+ , pandoc-types
188+ , envy
189+ , filepath
190+ , bytestring
191+ ghc-options: -threaded
192+ -rtsopts
193+ -with-rtsopts=-N
194+ other-modules:
195+ Control.Monad.Freer.GitHub
196+ CompaREST.GitHub.API
197+ CompaREST.GitHub.Action.Config
198+ GitHub.Data.Checks
199+ GitHub.Endpoints.Checks
176
200
177โˆ’test-suite openapi-diff-test
201+test-suite compaREST-tests
178 import: common-options
202 import: common-options
179 type: exitcode-stdio-1.0
203 type: exitcode-stdio-1.0
180 hs-source-dirs: test
204 hs-source-dirs: test
@@ -182,8 +206,10 @@ test-suite openapi-diff-test
182 other-modules:
206 other-modules:
183 Spec.Golden.Extra
207 Spec.Golden.Extra
184 Spec.Golden.TraceTree
208 Spec.Golden.TraceTree
209+ Paths_compaREST
210+ autogen-modules: Paths_compaREST
185 build-depends:
211 build-depends:
186โˆ’ , openapi-diff
212+ , compaREST
187 , tasty-golden
213 , tasty-golden
188 , tasty
214 , tasty
189 , bytestring
215 , bytestring
@@ -193,6 +219,7 @@ test-suite openapi-diff-test
193 , pandoc
219 , pandoc
194 , data-default
220 , data-default
195 , lens
221 , lens
222+ , pandoc-types
196 ghc-options: -threaded
223 ghc-options: -threaded
197 -rtsopts
224 -rtsopts
198 -with-rtsopts=-N
225 -with-rtsopts=-N
default.nixnew file+114
@@ -0,0 +1,114 @@
1+{ sources ? import ./nix/sources.nix
2+, haskellNix ? import sources.haskellNix { inherit system; }
3+, pkgs ? import haskellNix.sources.nixpkgs-2105 (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+
10+ hsPkgs = pkgs.haskell-nix.stackProject {
11+ src = nix-filter {
12+ root = ./.;
13+ name = "compaREST";
14+ include = [
15+ ./stack.yaml
16+ ./stack.yaml.lock
17+ ./compaREST.cabal
18+ ];
19+ };
20+
21+ modules = [
22+ {
23+ dontStrip = false;
24+ dontPatchELF = false;
25+ enableDeadCodeElimination = true;
26+ ghcOptions = [
27+ "-O2"
28+ "-fexpose-all-unfoldings"
29+ "-fspecialise-aggressively"
30+ ];
31+ packages.pandoc.ghcOptions = [ "-O1" ];
32+ packages.compaREST.src = nix-filter {
33+ root = ./.;
34+ name = "compaREST-src";
35+ include = with nix-filter; [
36+ (./compaREST.cabal)
37+ (inDirectory ./test)
38+ (inDirectory ./src)
39+ (inDirectory ./app)
40+ (inDirectory ./github-action)
41+ ./awsm-css/dist/awsm.min.css
42+ ./LICENSE
43+ ];
44+ };
45+ }
46+ ];
47+ };
48+
49+ staticify = name: drv: pkgs.runCommand name { } ''
50+ mkdir -p $out/bin
51+ cp ${drv + "/bin"}/* $out/bin
52+
53+ ${pkgs.nukeReferences}/bin/nuke-refs $out/bin/*
54+ '';
55+
56+ compaRESTBin = hsPkgs.compaREST.components.exes.compaREST;
57+ compaRESTStaticBin = (staticify "compaREST-static" hsPkgs.projectCross.musl64.hsPkgs.compaREST.components.exes.compaREST);
58+
59+
60+ # doesn't work
61+ armDarwinCompaREST = hsPkgs.projectCross.aarch64-darwin.hsPkgs.compaREST.components.exes.compaREST;
62+
63+ compaRESTImage = pkgs.dockerTools.buildImage {
64+ name = "compaREST";
65+ contents = [ compaRESTStaticBin ];
66+ config = {
67+ Entrypoint = [ "/bin/compaREST" ];
68+ };
69+ };
70+
71+ macOSCompaRESTBundle = pkgs.runCommand "compaREST-macOS-bundled"
72+ {
73+ buildInputs = [ masterPkgs.macdylibbundler ];
74+ } ''
75+ mkdir -p $out/lib
76+ cp ${compaRESTBin}/bin/compaREST $out/compaREST
77+ chmod 755 $out/compaREST
78+ dylibbundler -b \
79+ -x $out/compaREST \
80+ -d $out/lib \
81+ -p '@executable_path/lib'
82+ '';
83+
84+
85+ compaRESTGithubAction =
86+ let
87+ action = staticify "compaREST-GitHub-Action-static" hsPkgs.projectCross.musl64.hsPkgs.compaREST.components.exes.compaREST-GitHub-Action;
88+ wrapped = pkgs.runCommand "wrapped-compaREST-GitHub-Action" { buildInputs = [ pkgs.makeWrapper ]; } ''
89+ makeWrapper ${action}/bin/compaREST-GitHub-Action $out/bin/pre --add-flags "pre"
90+ makeWrapper ${action}/bin/compaREST-GitHub-Action $out/bin/run --add-flags "run"
91+ '';
92+ in
93+ pkgs.dockerTools.buildImage {
94+ name = "typeable/compaREST-GitHub-Action";
95+ tag = "latest";
96+ contents = [ wrapped pkgs.cacert ];
97+ };
98+
99+ WindowsCompaRESTBin = hsPkgs.projectCross.mingwW64.hsPkgs.compaREST.components.exes.compaREST;
100+in
101+{
102+ inherit
103+ compaRESTImage
104+ compaRESTGithubAction
105+ compaRESTStaticBin
106+ compaRESTBin
107+ hsPkgs
108+ macOSCompaRESTBundle
109+ WindowsCompaRESTBin;
110+
111+ # We use the static version so that we don't have to rebuild everything on CI.
112+ # The only binaries build on CI are static.
113+ test = hsPkgs.projectCross.musl64.hsPkgs.compaREST.components.tests.compaREST-tests;
114+}
docs/Development_guide.mdnew file+149
@@ -0,0 +1,149 @@
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.mdnew file+51
@@ -0,0 +1,51 @@
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+##### NOTE: will only work after public release
32+
33+```yaml
34+- uses: typeable/comparest
35+ with:
36+ old: old-openapi.yaml
37+ new: new-openapi.yaml
38+ project_name: TEST
39+```
40+
41+This will create a check on the pull request displaying the changes (if there are any) similar to this:
42+
43+![](img/github-action-report.png)
44+
45+The check will show success when there are no breaking changes, and be neutral otherwise.
46+
47+## Integrating into something other than Github Actions
48+
49+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.
50+
51+You see an example of CLI usage in our [readme](../README.md#assessing-compatibility-automatically).
docs/User_guide.mdnew file+51
@@ -0,0 +1,51 @@
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.pngnew file

Binary file not shown.

github-action/CompaREST/GitHub/API.hsnew file+86
@@ -0,0 +1,86 @@
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 $ "compaREST โ€“ " <> projectName
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 $ "compaREST โ€“ " <> projectName
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.hsnew file+43
@@ -0,0 +1,43 @@
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 System.Envy
12+
13+data Config = Config
14+ { githubToken :: GH.Auth
15+ , repoOwner :: GH.Name GH.Owner
16+ , repoName :: GH.Name GH.Repo
17+ , projectName :: Text
18+ , footerText :: Text
19+ , root :: FilePath
20+ , sha :: GH.Name GH.Commit
21+ }
22+
23+instance FromEnv Config where
24+ fromEnv _ = do
25+ token <- GH.OAuth <$> env "GITHUB_TOKEN"
26+ (owner, repo) <-
27+ T.split (== '/') <$> env "REPO" >>= \case
28+ [owner, name] -> pure (owner, name)
29+ _ -> fail "malformed repo"
30+ projectName <- env "PROJECT_NAME"
31+ footerText <- env "FOOTER"
32+ root <- envMaybe "ROOT" .!= "."
33+ sha <- env "SHA"
34+ pure $
35+ Config
36+ { githubToken = token
37+ , repoOwner = GH.mkName Proxy owner
38+ , repoName = GH.mkName Proxy repo
39+ , projectName = projectName
40+ , footerText = footerText
41+ , root = root
42+ , sha = GH.mkName Proxy sha
43+ }
github-action/Control/Monad/Freer/GitHub.hsnew file+27
@@ -0,0 +1,27 @@
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.hsnew file+173
@@ -0,0 +1,173 @@
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.hsnew file+12
@@ -0,0 +1,12 @@
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.hsnew file+66
@@ -0,0 +1,66 @@
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.Yaml.Aeson as Yaml
13+import qualified GitHub as GH
14+import System.Environment
15+import System.Envy (decodeEnv)
16+import System.FilePath ((</>))
17+import Text.Pandoc (runPure)
18+import Text.Pandoc.Builder
19+import Text.Pandoc.Options
20+import Text.Pandoc.Writers
21+
22+main :: IO ()
23+main = do
24+ cfg <- decodeEnv >>= either error pure
25+ getArgs >>= \case
26+ ["pre"] -> runPre cfg
27+ ["run"] -> do
28+ oldFile <- getEnv "OLD"
29+ newFile <- getEnv "NEW"
30+ runRun cfg (root cfg </> oldFile) (root cfg </> newFile)
31+ _ -> error "Invalid arguments."
32+
33+runner :: Config -> Eff '[GitHub, Error GH.Error, Reader Config, IO] a -> IO a
34+runner cfg =
35+ runM @IO . runReader cfg
36+ . flip (handleError @GH.Error) (error . displayException)
37+ . runGitHub (githubToken cfg)
38+
39+runPre :: Config -> IO ()
40+runPre cfg = runner cfg postStatusProcessing
41+
42+runRun :: Config -> FilePath -> FilePath -> IO ()
43+runRun cfg old' new' = runner cfg $ do
44+ old <- Yaml.decodeFileThrow old'
45+ new <- Yaml.decodeFileThrow new'
46+ let reportConfig =
47+ ReportConfig
48+ { treeStyle = FoldingBlockquotesTreeStyle
49+ , reportMode = All
50+ }
51+ (report, status) = runReport reportConfig (old, new)
52+
53+ body = markdown report <> "\n\n" <> footerText cfg
54+
55+ result =
56+ if old == new
57+ then Nothing
58+ else Just (body, status)
59+ postStatus result
60+
61+markdown :: Blocks -> Text
62+markdown =
63+ either (error . displayException) id
64+ . runPure
65+ . writeHtml5String def
66+ . doc
nix/ci.nixnew file+6
@@ -0,0 +1,6 @@
1+{ sources ? import ./sources.nix
2+, pkgs ? import sources.nixpkgs { }
3+}:
4+pkgs.mkShell {
5+ packages = [ pkgs.haskellPackages.fourmolu ];
6+}
nix/sources.jsonnew file+50
@@ -0,0 +1,50 @@
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": "c8a8f57de3c14fcf1c5cda045ed885fb6d06a00e",
9+ "sha256": "0frzmvz8rhb4vgx20qms9xxqbkmg5pwivis4qis63ny5wxclzb34",
10+ "type": "tarball",
11+ "url": "https://github.com/input-output-hk/haskell.nix/archive/c8a8f57de3c14fcf1c5cda045ed885fb6d06a00e.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": "5830a4dd348d77e39a0f3c4c762ff2663b602d4c",
21+ "sha256": "1d3lsrqvci4qz2hwjrcnd8h5vfkg8aypq3sjd4g3izbc8frwz5sm",
22+ "type": "tarball",
23+ "url": "https://github.com/nmattia/niv/archive/5830a4dd348d77e39a0f3c4c762ff2663b602d4c.tar.gz",
24+ "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
25+ },
26+ "nix-filter": {
27+ "branch": "master",
28+ "description": "a small self-container source filtering lib",
29+ "homepage": "",
30+ "owner": "numtide",
31+ "repo": "nix-filter",
32+ "rev": "3c9e33ed627e009428197b07216613206f06ed80",
33+ "sha256": "19w142crrkywxynmyw4rhz4nglrg64yjawfkw3j91qwkwbfjds84",
34+ "type": "tarball",
35+ "url": "https://github.com/numtide/nix-filter/archive/3c9e33ed627e009428197b07216613206f06ed80.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": "f0fe5e9ba08e658474476317717aca7acacef4d4",
45+ "sha256": "1n5syynwmg3x8lj2kyb2ilbxw5nmw7f2kw6yxsi8qadx91hrvkwm",
46+ "type": "tarball",
47+ "url": "https://github.com/NixOS/nixpkgs/archive/f0fe5e9ba08e658474476317717aca7acacef4d4.tar.gz",
48+ "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
49+ }
50+}
nix/sources.nixnew file+174
@@ -0,0 +1,174 @@
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); }
policeman.jsonnew file+6
@@ -0,0 +1,6 @@
1+[{
2+ "file": "compaREST.cabal",
3+ "annotation_level": "notice",
4+ "end_line": 0,
5+ "start_line": 0
6+}]
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/OpenAPI/Checker/Behavior.hs โ†’ src/Data/OpenApi/Compare/Behavior.hsrenamed+49โˆ’11
@@ -1,18 +1,22 @@
1โˆ’module OpenAPI.Checker.Behavior
1+module Data.OpenApi.Compare.Behavior
2โˆ’ ( BehaviorLevel (..)
2+ ( BehaviorLevel (..),
3โˆ’ , Behavable (..)
3+ Behavable (..),
4+ IssueKind (..),
4โˆ’ , Issuable (..)
5+ Issuable (..),
5โˆ’ , Orientation (..)
6+ Orientation (..),
6โˆ’ , toggleOrientation
7+ toggleOrientation,
7โˆ’ , Behavior
8+ Behavior,
8โˆ’ , AnIssue (..)
9+ AnIssue (..),
10+ withClass,
11+ anIssueKind,
12+ relatedAnIssues,
9 )
13 )
10where
14where
11
15
12import Data.Aeson
16import Data.Aeson
13import Data.Kind
17import Data.Kind
18+import Data.OpenApi.Compare.Paths
14import Data.Typeable
19import Data.Typeable
15โˆ’import OpenAPI.Checker.Paths
16import Text.Pandoc.Builder
20import Text.Pandoc.Builder
17
21
18-- | Kind
22-- | Kind
@@ -39,10 +43,23 @@ class
39 Behavable (a :: BehaviorLevel) (b :: BehaviorLevel)
43 Behavable (a :: BehaviorLevel) (b :: BehaviorLevel)
40 where
44 where
41 data Behave a b
45 data Behave a b
42โˆ’ describeBehaviour :: Behave a b -> Inlines
46+ describeBehavior :: Behave a b -> Inlines
43
47
44type instance AdditionalQuiverConstraints Behave a b = Behavable a b
48type instance AdditionalQuiverConstraints Behave a b = Behavable a b
45
49
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+
46class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLevel) where
63class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLevel) where
47 data Issue l :: Type
64 data Issue l :: Type
48
65
@@ -58,7 +75,22 @@ class (Typeable l, Ord (Issue l), Show (Issue l)) => Issuable (l :: BehaviorLeve
58 -- If _producer_ doesn't have something, the element was "added".
75 -- If _producer_ doesn't have something, the element was "added".
59 describeIssue :: Orientation -> Issue l -> Blocks
76 describeIssue :: Orientation -> Issue l -> Blocks
60
77
61โˆ’ issueIsUnsupported :: Issue l -> Bool
78+ issueKind :: Issue l -> IssueKind
79+
80+ -- | An equivalence relation designating whether two issues are talking about the aspect of the schema. This is used
81+ -- to remove duplicates from the "reverse" error tree we get when we look for non-breaking changes.
82+ -- Generally if checking X->Y raises issue I, and checking Y->X raises issue J, I and J should be related.
83+ relatedIssues :: Issue l -> Issue l -> Bool
84+ relatedIssues = (==)
85+
86+-- Utility function for 'relatedIssues'. In @withClass eq f@, @f@ attempts to partition inputs into equivalence classes,
87+-- and two items in the same equivalence class are related. If both items aren't assigned to a class by @f@, instead
88+-- @eq@ is used to compare them.
89+withClass :: Eq b => (a -> a -> Bool) -> (a -> Maybe b) -> a -> a -> Bool
90+withClass eq f = \x y -> case (f x, f y) of
91+ (Just fx, Just fy) -> fx == fy
92+ (Nothing, Nothing) -> eq x y
93+ (_, _) -> False
62
94
63data Orientation = Forward | Backward
95data Orientation = Forward | Backward
64 deriving stock (Eq, Ord, Show)
96 deriving stock (Eq, Ord, Show)
@@ -84,3 +116,9 @@ deriving stock instance Ord (AnIssue l)
84
116
85instance ToJSON (AnIssue l) where
117instance ToJSON (AnIssue l) where
86 toJSON (AnIssue _ issue) = toJSON issue
118 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/OpenAPI/Checker/Common.hs โ†’ src/Data/OpenApi/Compare/Common.hsrenamed+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’module OpenAPI.Checker.Common
1+module Data.OpenApi.Compare.Common
2โˆ’ ( zipAll
2+ ( zipAll,
3 )
3 )
4where
4where
5
5
src/OpenAPI/Checker/Formula.hs โ†’ src/Data/OpenApi/Compare/Formula.hsrenamed+35โˆ’25
@@ -1,20 +1,21 @@
1โˆ’module OpenAPI.Checker.Formula
1+module Data.OpenApi.Compare.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+ )
11โˆ’ ) where
12+where
12
13
13import Data.Kind
14import Data.Kind
14โˆ’import Data.Monoid
15import qualified Data.List.NonEmpty as NE
15import qualified Data.List.NonEmpty as NE
16โˆ’import OpenAPI.Checker.Paths
16+import Data.Monoid
17+import Data.OpenApi.Compare.Paths
17โˆ’import qualified OpenAPI.Checker.PathsPrefixTree as P
18+import qualified Data.OpenApi.Compare.PathsPrefixTree as P
18
19
19type VarRef = Int
20type VarRef = Int
20
21
@@ -24,15 +25,24 @@ type VarRef = Int
24-- fixpoints always exist, i.e. that @x = f x@ has at least one solution.
25-- fixpoints always exist, i.e. that @x = f x@ has at least one solution.
25data FormulaF (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: Type) where
26data FormulaF (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: Type) where
26 Result :: a -> FormulaF q f r a
27 Result :: a -> FormulaF q f r a
28+ Errors ::
27โˆ’ Errors :: !(P.PathsPrefixTree q f r) -> FormulaF q f r a
29+ !(P.PathsPrefixTree q f r) ->
28โˆ’ -- ^ invariant: never empty
30+ -- | invariant: never empty
31+ FormulaF q f r a
32+ Apply ::
29โˆ’ Apply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
33+ FormulaF q f r (b -> c) ->
34+ FormulaF q f r b ->
35+ (c -> a) ->
30โˆ’ -- ^ invariant: at least one of LHS and RHS is not 'Errors', and they are
36+ -- | invariant: at least one of LHS and RHS is not 'Errors', and they are
31 -- both not 'Result'
37 -- both not 'Result'
38+ FormulaF q f r a
39+ SelectFirst ::
32โˆ’ SelectFirst :: NE.NonEmpty (SomeFormulaF b)
40+ NE.NonEmpty (SomeFormulaF b) ->
33โˆ’ -> !(P.PathsPrefixTree q f r) -> (b -> a) -> FormulaF q f r a
41+ !(P.PathsPrefixTree q f r) ->
42+ (b -> a) ->
34โˆ’ -- ^ invariant: the list doesn't contain any 'Result's, 'Errors' or
43+ -- | invariant: the list doesn't contain any 'Result's, 'Errors' or
35 -- 'SelectFirst'
44 -- 'SelectFirst'
45+ FormulaF q f r a
36 Variable :: !VarRef -> a -> FormulaF q f r a
46 Variable :: !VarRef -> a -> FormulaF q f r a
37
47
38mkApply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
48mkApply :: FormulaF q f r (b -> c) -> FormulaF q f r b -> (c -> a) -> FormulaF q f r a
@@ -44,13 +54,13 @@ mkApply f x h = Apply f x h
44mkSelectFirst :: [SomeFormulaF b] -> P.PathsPrefixTree q f r -> (b -> a) -> FormulaF q f r a
54mkSelectFirst :: [SomeFormulaF b] -> P.PathsPrefixTree q f r -> (b -> a) -> FormulaF q f r a
45mkSelectFirst fs allE h = case foldMap check fs of
55mkSelectFirst fs allE h = case foldMap check fs of
46 (First (Just x), _) -> Result (h x)
56 (First (Just x), _) -> Result (h x)
47โˆ’ (First Nothing, x:xs) -> SelectFirst (x NE.:| xs) allE h
57+ (First Nothing, x : xs) -> SelectFirst (x NE.:| xs) allE h
48 (First Nothing, []) -> Errors allE
58 (First Nothing, []) -> Errors allE
49 where
59 where
50 check (SomeFormulaF (Result x)) = (First (Just x), mempty)
60 check (SomeFormulaF (Result x)) = (First (Just x), mempty)
51 check (SomeFormulaF (Errors _)) = (mempty, mempty)
61 check (SomeFormulaF (Errors _)) = (mempty, mempty)
52โˆ’ check (SomeFormulaF (SelectFirst xs _ h'))
62+ check (SomeFormulaF (SelectFirst xs _ h')) =
53โˆ’ = (mempty, NE.toList (fmap (fmap h') xs))
63+ (mempty, NE.toList (fmap (fmap h') xs))
54 check x = (mempty, [x])
64 check x = (mempty, [x])
55
65
56data SomeFormulaF (a :: Type) where
66data SomeFormulaF (a :: Type) where
@@ -96,7 +106,7 @@ calculate (Apply f x h) = case calculate f of
96 Right x' -> Right (h (f' x'))
106 Right x' -> Right (h (f' x'))
97calculate (SelectFirst xs e h) = go (NE.toList xs)
107calculate (SelectFirst xs e h) = go (NE.toList xs)
98 where
108 where
99โˆ’ go (SomeFormulaF r:rs) = case calculate r of
109+ go (SomeFormulaF r : rs) = case calculate r of
100 Left _ -> go rs
110 Left _ -> go rs
101 Right x -> Right (h x)
111 Right x -> Right (h x)
102 go [] = Left e
112 go [] = Left e
src/Data/OpenApi/Compare/Memo.hsnew file+164
@@ -0,0 +1,164 @@
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/OpenAPI/Checker/Orphans.hs โ†’ src/Data/OpenApi/Compare/Orphans.hsrenamed+1โˆ’1
@@ -1,6 +1,6 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Orphans () where
3+module Data.OpenApi.Compare.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/OpenAPI/Checker/Paths.hs โ†’ src/Data/OpenApi/Compare/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 OpenAPI.Checker.Paths
4+module Data.OpenApi.Compare.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/OpenAPI/Checker/PathsPrefixTree.hs โ†’ src/Data/OpenApi/Compare/PathsPrefixTree.hsrenamed+60โˆ’38
@@ -1,20 +1,22 @@
1{-# LANGUAGE QuantifiedConstraints #-}
1{-# LANGUAGE QuantifiedConstraints #-}
2
2
3โˆ’module OpenAPI.Checker.PathsPrefixTree
3+module Data.OpenApi.Compare.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,
13โˆ’ , takeSubtree
14+ takeSubtree,
15+ lookup,
14โˆ’ , embed
16+ embed,
15โˆ’ , size
17+ size,
16โˆ’ , partition
18+ partition,
17โˆ’ , map
19+ map,
18 )
20 )
19where
21where
20
22
@@ -25,15 +27,15 @@ import qualified Data.HashMap.Strict as HM
25import Data.Kind
27import Data.Kind
26import qualified Data.Map as M
28import qualified Data.Map as M
27import Data.Monoid
29import Data.Monoid
30+import Data.OpenApi.Compare.Paths
28import qualified Data.Set as S
31import qualified Data.Set as S
29import qualified Data.Text as T
32import qualified Data.Text as T
30import Data.Type.Equality
33import Data.Type.Equality
31import qualified Data.TypeRepMap as TRM
34import qualified Data.TypeRepMap as TRM
32import qualified Data.Vector as V
35import qualified Data.Vector as V
33import qualified GHC.Exts as Exts
36import qualified GHC.Exts as Exts
34โˆ’import OpenAPI.Checker.Paths
35import Type.Reflection
37import Type.Reflection
36โˆ’import Prelude hiding (filter, map, null)
38+import Prelude hiding (filter, lookup, map, null)
37
39
38-- | A list of @AnItem r f@, but optimized into a prefix tree.
40-- | A list of @AnItem r f@, but optimized into a prefix tree.
39data PathsPrefixTree (q :: k -> k -> Type) (f :: k -> Type) (r :: k) = PathsPrefixTree
41data PathsPrefixTree (q :: k -> k -> Type) (f :: k -> Type) (r :: k) = PathsPrefixTree
@@ -56,10 +58,27 @@ filter f (PathsPrefixTree roots branches) = PathsPrefixTree roots' branches'
56 roots' = filterASet f roots
58 roots' = filterASet f roots
57 branches' =
59 branches' =
58 Exts.fromList
60 Exts.fromList
59โˆ’ . fmap (\(TRM.WrapTypeable (AStep x)) -> TRM.WrapTypeable . AStep . M.filter (not . null) . fmap (filter f) $ x)
61+ . fmap (\(TRM.WrapTypeable (AStep x)) -> TRM.WrapTypeable . AStep . M.mapMaybe (maybeNonEmpty . filter f) $ x)
60 . Exts.toList
62 . Exts.toList
61 $ branches
63 $ branches
62
64
65+ maybeNonEmpty = mfilter (not . null) . Just
66+
67+filterWithKey :: (forall a. Paths q r a -> f a -> Bool) -> PathsPrefixTree q f r -> PathsPrefixTree q f r
68+filterWithKey = go Root
69+ where
70+ go :: Paths q r b -> (forall a. Paths q r a -> f a -> Bool) -> PathsPrefixTree q f b -> PathsPrefixTree q f b
71+ go xs f (PathsPrefixTree roots branches) = PathsPrefixTree roots' branches'
72+ where
73+ roots' = filterASet (f xs) roots
74+ branches' =
75+ Exts.fromList
76+ . fmap (\(TRM.WrapTypeable (AStep x)) -> TRM.WrapTypeable . AStep . M.mapMaybeWithKey (\k -> maybeNonEmpty . go (xs `Snoc` k) f) $ x)
77+ . Exts.toList
78+ $ branches
79+
80+ maybeNonEmpty = mfilter (not . null) . Just
81+
63-- | The number of leaves.
82-- | The number of leaves.
64size :: PathsPrefixTree q f r -> Int
83size :: PathsPrefixTree q f r -> Int
65size (PathsPrefixTree root branches) =
84size (PathsPrefixTree root branches) =
@@ -83,11 +102,11 @@ deriving stock instance Eq (PathsPrefixTree q f a)
83
102
84-- Kind of orphan. Treat the map as an infinite tuple of @Maybe (f a)@'s, where
103-- Kind of orphan. Treat the map as an infinite tuple of @Maybe (f a)@'s, where
85-- the components are ordered by the @SomeTypeRep@ of the @a@.
104-- the components are ordered by the @SomeTypeRep@ of the @a@.
86โˆ’compareTRM
105+compareTRM ::
87โˆ’ :: (forall a. Typeable a => Ord (f a))
106+ (forall a. Typeable a => Ord (f a)) =>
88โˆ’ => TRM.TypeRepMap f
107+ TRM.TypeRepMap f ->
89โˆ’ -> TRM.TypeRepMap f
108+ TRM.TypeRepMap f ->
90โˆ’ -> Ordering
109+ Ordering
91compareTRM s1 s2 =
110compareTRM s1 s2 =
92 foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
111 foldMap (\k -> compareMaybe compareW (M.lookup k m1) (M.lookup k m2)) mKeys
93 where
112 where
@@ -97,11 +116,11 @@ compareTRM s1 s2 =
97 compareMaybe _ Nothing (Just _) = LT
116 compareMaybe _ Nothing (Just _) = LT
98 compareMaybe _ (Just _) Nothing = GT
117 compareMaybe _ (Just _) Nothing = GT
99 compareMaybe cmp (Just x) (Just y) = cmp x y
118 compareMaybe cmp (Just x) (Just y) = cmp x y
100โˆ’ compareW
119+ compareW ::
101โˆ’ :: (forall a. Typeable a => Ord (f a))
120+ (forall a. Typeable a => Ord (f a)) =>
102โˆ’ => TRM.WrapTypeable f
121+ TRM.WrapTypeable f ->
103โˆ’ -> TRM.WrapTypeable f
122+ TRM.WrapTypeable f ->
104โˆ’ -> Ordering
123+ Ordering
105 compareW (TRM.WrapTypeable (x :: f a)) (TRM.WrapTypeable (y :: f b))
124 compareW (TRM.WrapTypeable (x :: f a)) (TRM.WrapTypeable (y :: f b))
106 | Just Refl <- testEquality (typeRep @a) (typeRep @b) = compare x y
125 | Just Refl <- testEquality (typeRep @a) (typeRep @b) = compare x y
107 | otherwise = EQ -- unreachable
126 | otherwise = EQ -- unreachable
@@ -153,10 +172,10 @@ instance Monoid (ASet a) where
153 mempty = AnEmptySet
172 mempty = AnEmptySet
154
173
155data AStep (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: k) where
174data AStep (q :: k -> k -> Type) (f :: k -> Type) (r :: k) (a :: k) where
156โˆ’ AStep
175+ AStep ::
157โˆ’ :: NiceQuiver q r a =>
176+ NiceQuiver q r a =>
158โˆ’ !(M.Map (q r a) (PathsPrefixTree q f a))
177+ !(M.Map (q r a) (PathsPrefixTree q f a)) ->
159โˆ’ -> AStep q f r a
178+ AStep q f r a
160
179
161mapAStep :: (forall x. f x -> f x) -> AStep q f r a -> AStep q f r a
180mapAStep :: (forall x. f x -> f x) -> AStep q f r a -> AStep q f r a
162mapAStep f (AStep m) = AStep $ M.map (map f) m
181mapAStep f (AStep m) = AStep $ M.map (map f) m
@@ -196,12 +215,12 @@ null :: PathsPrefixTree q f r -> Bool
196null (PathsPrefixTree AnEmptySet s) = all (\(TRM.WrapTypeable (AStep x)) -> all null x) (Exts.toList s)
215null (PathsPrefixTree AnEmptySet s) = all (\(TRM.WrapTypeable (AStep x)) -> all null x) (Exts.toList s)
197null _ = False
216null _ = False
198
217
199โˆ’foldWith
218+foldWith ::
200โˆ’ :: forall q f m r.
219+ forall q f m r.
201โˆ’ Monoid m
220+ Monoid m =>
202โˆ’ => (forall a. Ord (f a) => Paths q r a -> f a -> m)
221+ (forall a. Ord (f a) => Paths q r a -> f a -> m) ->
203โˆ’ -> PathsPrefixTree q f r
222+ PathsPrefixTree q f r ->
204โˆ’ -> m
223+ m
205foldWith k = goTPT Root
224foldWith k = goTPT Root
206 where
225 where
207 goTPT :: forall a. Paths q r a -> PathsPrefixTree q f a -> m
226 goTPT :: forall a. Paths q r a -> PathsPrefixTree q f a -> m
@@ -225,6 +244,9 @@ takeSubtree (Snoc xs x) t =
225 foldMap (\(AStep m) -> fold $ M.lookup x m) $
244 foldMap (\(AStep m) -> fold $ M.lookup x m) $
226 TRM.lookup @a $ snocItems $ takeSubtree xs t
245 TRM.lookup @a $ snocItems $ takeSubtree xs t
227
246
247+lookup :: Paths q r a -> PathsPrefixTree q f r -> S.Set (f a)
248+lookup xs = toSet . rootItems . takeSubtree xs
249+
228-- | Embed a subtree in a larger tree with given prefix
250-- | Embed a subtree in a larger tree with given prefix
229embed :: Paths q r a -> PathsPrefixTree q f a -> PathsPrefixTree q f r
251embed :: Paths q r a -> PathsPrefixTree q f a -> PathsPrefixTree q f r
230embed Root t = t
252embed Root t = t
src/OpenAPI/Checker/References.hs โ†’ src/Data/OpenApi/Compare/References.hsrenamed+11โˆ’12
@@ -1,9 +1,9 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.References
3+module Data.OpenApi.Compare.References
4โˆ’ ( Step (..)
4+ ( Step (..),
5โˆ’ , dereference
5+ dereference,
6โˆ’ , Typeable
6+ Typeable,
7 )
7 )
8where
8where
9
9
@@ -11,19 +11,18 @@ 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.Typeable
14+import Data.OpenApi.Compare.Orphans ()
15โˆ’import OpenAPI.Checker.Orphans ()
16โˆ’import OpenAPI.Checker.Subtree
15+import Data.OpenApi.Compare.Subtree
17
16
18instance Typeable a => Steppable (Referenced a) a where
17instance Typeable a => Steppable (Referenced a) a where
19 data Step (Referenced a) a = InlineStep
18 data Step (Referenced a) a = InlineStep
20 deriving stock (Eq, Ord, Show)
19 deriving stock (Eq, Ord, Show)
21
20
22โˆ’dereference
21+dereference ::
23โˆ’ :: Typeable a
22+ Typeable a =>
24โˆ’ => Traced (Definitions a)
23+ Traced (Definitions a) ->
25โˆ’ -> Traced (Referenced a)
24+ Traced (Referenced a) ->
26โˆ’ -> Traced a
25+ Traced a
27dereference defs x = case extract x of
26dereference defs x = case extract x of
28 Inline a ->
27 Inline a ->
29 traced (ask x >>> step InlineStep) a
28 traced (ask x >>> step InlineStep) a
src/OpenAPI/Checker/Report.hs โ†’ src/Data/OpenApi/Compare/Report.hsrenamed+177โˆ’120
@@ -1,11 +1,13 @@
1โˆ’module OpenAPI.Checker.Report
1+module Data.OpenApi.Compare.Report
2โˆ’ ( generateReport
2+ ( generateReport,
3+ CheckerOutput (..),
3โˆ’ , ReportInput (..)
4+ ReportInput (..),
5+ segregateIssues,
4โˆ’ , ReportStatus (..)
6+ ReportStatus (..),
5โˆ’ , Pandoc
7+ Pandoc,
6โˆ’ , ReportConfig (..)
8+ ReportConfig (..),
7โˆ’ , ReportTreeStyle (..)
9+ ReportTreeStyle (..),
8โˆ’ , ReportMode (..)
10+ ReportMode (..),
9 )
11 )
10where
12where
11
13
@@ -21,6 +23,15 @@ import qualified Data.Map as M
21import Data.Map.Ordered (OMap)
23import Data.Map.Ordered (OMap)
22import qualified Data.Map.Ordered as OM
24import qualified Data.Map.Ordered as OM
23import Data.Maybe
25import 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
24import Data.OpenUnion
35import Data.OpenUnion
25import Data.OpenUnion.Extra
36import Data.OpenUnion.Extra
26import Data.Set
37import Data.Set
@@ -30,27 +41,47 @@ import qualified Data.Text as T
30import Data.TypeRepMap hiding (empty)
41import Data.TypeRepMap hiding (empty)
31import Data.Typeable
42import Data.Typeable
32import Generic.Data
43import 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
42import Text.Pandoc.Builder
44import Text.Pandoc.Builder
43
45
44type Changes = P.PathsPrefixTree Behave AnIssue 'APILevel
46type Changes = P.PathsPrefixTree Behave AnIssue 'APILevel
45
47
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+
46data ReportInput = ReportInput
56data ReportInput = ReportInput
57+ { -- | forward 'CertainIssue', 'ProbablyIssue' and 'Comment'
47โˆ’ { breakingChanges :: Changes
58+ breakingChanges :: Changes
59+ , -- | backward 'CertainIssue', 'ProbablyIssue' and 'Comment', except those shadowed by 'relatedIssues'
48โˆ’ , nonBreakingChanges :: Changes
60+ nonBreakingChanges :: Changes
61+ , -- | forward and backward 'Unsupported' (assumed to be the same anyway)
62+ unsupportedChanges :: Changes
63+ , -- | forward and backward 'SchemaInvalid' (assumed to be the same anyway)
64+ schemaIssues :: Changes
49 }
65 }
50 deriving stock (Generic)
66 deriving stock (Generic)
51 deriving (Semigroup, Monoid) via (Generically ReportInput)
67 deriving (Semigroup, Monoid) via (Generically ReportInput)
52 deriving anyclass (ToJSON)
68 deriving anyclass (ToJSON)
53
69
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+
54data ReportStatus
85data ReportStatus
55 = BreakingChanges
86 = BreakingChanges
56 | NoBreakingChanges
87 | NoBreakingChanges
@@ -78,49 +109,66 @@ data ReportTreeStyle = HeadersTreeStyle | FoldingBlockquotesTreeStyle
78twoRowTable :: [(Inlines, Inlines)] -> Blocks
109twoRowTable :: [(Inlines, Inlines)] -> Blocks
79twoRowTable x = simpleTable (para . fst <$> x) [para . snd <$> x]
110twoRowTable x = simpleTable (para . fst <$> x) [para . snd <$> x]
80
111
81โˆ’generateReport :: ReportConfig -> ReportInput -> (Pandoc, ReportStatus)
112+generateReport :: ReportConfig -> ReportInput -> (Blocks, ReportStatus)
82generateReport cfg inp =
113generateReport cfg inp =
83โˆ’ let partitionUnsupported = P.partition (\(AnIssue _ i) -> issueIsUnsupported i)
114+ let schemaIssuesPresent = not $ P.null $ schemaIssues inp
84โˆ’ (bUnsupported, breaking) = partitionUnsupported $ breakingChanges inp
115+ breakingChangesPresent = not $ P.null $ breakingChanges inp
85โˆ’ (nbUnsupported, invertIssueOrientationP -> nonBreaking) = partitionUnsupported $ nonBreakingChanges inp
86โˆ’ unsupported = bUnsupported <> nbUnsupported
87โˆ’ breakingChangesPresent = not $ P.null breaking
88โˆ’ nonBreakingChangesPresent = not $ P.null nonBreaking
116+ nonBreakingChangesPresent = not $ P.null $ nonBreakingChanges inp
89โˆ’ unsupportedChangesPresent = not $ P.null unsupported
117+ unsupportedChangesPresent = not $ P.null $ unsupportedChanges inp
90 nonBreakingChangesShown = case reportMode cfg of
118 nonBreakingChangesShown = case reportMode cfg of
91 All -> True
119 All -> True
92 OnlyErrors -> False
120 OnlyErrors -> False
93 builder = buildReport cfg
121 builder = buildReport cfg
94 report =
122 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
99โˆ’ , show' $ P.size breaking
127+ [
100โˆ’ )
128+ ( refOpt schemaIssuesPresent schemaIssuesId "โ€ผ๏ธ Schema issues"
101โˆ’ ]
129+ , show' $ P.size $ schemaIssues inp
102โˆ’ ++ when'
130+ )
103โˆ’ nonBreakingChangesShown
131+ ]
104โˆ’ [ ( refOpt nonBreakingChangesPresent nonBreakingChangesId "๐Ÿ™† Non-breaking changes"
132+ ++ [
105โˆ’ , show' $ P.size nonBreaking
133+ ( refOpt breakingChangesPresent breakingChangesId "โŒ Breaking changes"
134+ , show' $ P.size $ breakingChanges inp
106 )
135 )
107 ]
136 ]
108โˆ’ ++ [ ( refOpt unsupportedChangesPresent unsupportedChangesId "๐Ÿคท Unsupported feature changes"
137+ ++ when'
109โˆ’ , show' $ P.size unsupported
138+ nonBreakingChangesShown
110โˆ’ )
139+ [
111โˆ’ ])
140+ ( refOpt nonBreakingChangesPresent nonBreakingChangesId "โš ๏ธ Non-breaking changes"
112โˆ’ <> when'
141+ , show' $ P.size $ nonBreakingChanges inp
113โˆ’ breakingChangesPresent
142+ )
114โˆ’ (header 1 (anchor breakingChangesId <> "โš ๏ธ Breaking changes")
143+ ]
115โˆ’ <> builder (showErrs breaking))
144+ ++ when'
116โˆ’ <> when'
145+ unsupportedChangesPresent
117โˆ’ (nonBreakingChangesPresent && nonBreakingChangesShown)
146+ [
118โˆ’ (header 1 (anchor nonBreakingChangesId <> "๐Ÿ™† Non-breaking changes")
147+ ( refOpt unsupportedChangesPresent unsupportedChangesId "โ“ Unsupported feature changes"
119โˆ’ <> builder (showErrs nonBreaking))
148+ , show' $ P.size $ unsupportedChanges inp
120โˆ’ <> when'
149+ )
121โˆ’ unsupportedChangesPresent
150+ ]
122โˆ’ (header 1 (anchor unsupportedChangesId <> "๐Ÿคท Unsupported feature changes")
151+ )
123โˆ’ <> builder (showErrs unsupported))
152+ <> when'
153+ schemaIssuesPresent
154+ ( header 1 (anchor schemaIssuesId <> "โ€ผ๏ธ Schema issues")
155+ <> builder (showErrs $ schemaIssues inp)
156+ )
157+ <> when'
158+ breakingChangesPresent
159+ ( header 1 (anchor breakingChangesId <> "โŒ Breaking changes")
160+ <> builder (showErrs $ breakingChanges inp)
161+ )
162+ <> when'
163+ (nonBreakingChangesPresent && nonBreakingChangesShown)
164+ ( header 1 (anchor nonBreakingChangesId <> "โš ๏ธ Non-breaking changes")
165+ <> builder (showErrs $ nonBreakingChanges inp)
166+ )
167+ <> when'
168+ unsupportedChangesPresent
169+ ( header 1 (anchor unsupportedChangesId <> "โ“ Unsupported feature changes")
170+ <> builder (showErrs $ unsupportedChanges inp)
171+ )
124 status =
172 status =
125 if
173 if
126 | breakingChangesPresent -> BreakingChanges
174 | breakingChangesPresent -> BreakingChanges
@@ -135,10 +183,11 @@ generateReport cfg inp =
135 refOpt False _ i = i
183 refOpt False _ i = i
136 refOpt True a i = link ("#" <> a) "" i
184 refOpt True a i = link ("#" <> a) "" i
137
185
138โˆ’ breakingChangesId, nonBreakingChangesId, unsupportedChangesId :: Text
186+ breakingChangesId, nonBreakingChangesId, unsupportedChangesId, schemaIssuesId :: Text
139 breakingChangesId = "breaking-changes"
187 breakingChangesId = "breaking-changes"
140 unsupportedChangesId = "unsupported-changes"
188 unsupportedChangesId = "unsupported-changes"
141 nonBreakingChangesId = "non-breaking-changes"
189 nonBreakingChangesId = "non-breaking-changes"
190+ schemaIssuesId = "schema-issues"
142
191
143 when' :: Monoid m => Bool -> m -> m
192 when' :: Monoid m => Bool -> m -> m
144 when' True m = m
193 when' True m = m
@@ -149,13 +198,14 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
149 let -- Extract this pattern if more cases like this arise
198 let -- Extract this pattern if more cases like this arise
150 ( removedPaths :: Maybe (Orientation, [Issue 'APILevel])
199 ( removedPaths :: Maybe (Orientation, [Issue 'APILevel])
151 , otherIssues :: Set (AnIssue a)
200 , otherIssues :: Set (AnIssue a)
152โˆ’ ) = case eqT @a @'APILevel of
201+ ) = case eqT @a @ 'APILevel of
153 Just Refl
202 Just Refl
154 | (S.toList -> p@((AnIssue ori _) : _), o) <-
203 | (S.toList -> p@((AnIssue ori _) : _), o) <-
155 S.partition
204 S.partition
156โˆ’ (\((AnIssue _ u)) -> case u of
205+ ( \((AnIssue _ u)) -> case u of
157โˆ’ NoPathsMatched {} -> True
206+ NoPathsMatched {} -> True
158โˆ’ AllPathsFailed {} -> True)
207+ AllPathsFailed {} -> True
208+ )
159 currentIssues ->
209 currentIssues ->
160 let p' = p <&> (\(AnIssue _ i) -> i)
210 let p' = p <&> (\(AnIssue _ i) -> i)
161 in (Just (ori, p'), o)
211 in (Just (ori, p'), o)
@@ -166,9 +216,10 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
166 paths = case removedPaths of
216 paths = case removedPaths of
167 Just (ori, ps) -> do
217 Just (ori, ps) -> do
168 singletonHeader
218 singletonHeader
169โˆ’ (case ori of
219+ ( case ori of
170โˆ’ Forward -> "Removed paths"
220+ Forward -> "Removed paths"
171โˆ’ Backward -> "Added paths")
221+ Backward -> "Added paths"
222+ )
172 $ singletonBody $
223 $ singletonBody $
173 bulletList $
224 bulletList $
174 ps <&> \case
225 ps <&> \case
@@ -180,7 +231,7 @@ showErrs x@(P.PathsPrefixNode currentIssues _) =
180 flip foldMap (M.toList m) $ \(bhv, subErrors) ->
231 flip foldMap (M.toList m) $ \(bhv, subErrors) ->
181 if P.null subErrors
232 if P.null subErrors
182 then mempty
233 then mempty
183โˆ’ else singletonHeader (describeBehaviour bhv) $ showErrs subErrors
234+ else singletonHeader (describeBehavior bhv) $ showErrs subErrors
184 in issues <> paths <> rest
235 in issues <> paths <> rest
185
236
186unfoldFunctions :: forall m a. (Monoid m, Eq a) => a -> [a -> (m, a)] -> (a -> m) -> m
237unfoldFunctions :: forall m a. (Monoid m, Eq a) => a -> [a -> (m, a)] -> (a -> m) -> m
@@ -198,66 +249,68 @@ jets =
198 unwrapReportJetResult
249 unwrapReportJetResult
199 <$> [ constructReportJet $
250 <$> [ constructReportJet $
200 curry $ \case
251 curry $ \case
201โˆ’ (OfType Object, p@(InPartition _)) -> Just $ describeBehaviour p :: Maybe Inlines
252+ (OfType Object, p@(InPartition _)) -> Just $ describeBehavior p :: Maybe Inlines
202 _ -> Nothing
253 _ -> Nothing
203 , constructReportJet jsonPathJet
254 , constructReportJet jsonPathJet
204 , constructReportJet $ \p@(AtPath _) op@(InOperation _) ->
255 , constructReportJet $ \p@(AtPath _) op@(InOperation _) ->
205โˆ’ strong (describeBehaviour op) <> " " <> describeBehaviour p :: Inlines
256+ strong (describeBehavior op) <> " " <> describeBehavior p :: Inlines
206 , constructReportJet $ \(WithStatusCode c) ResponsePayload PayloadSchema ->
257 , constructReportJet $ \(WithStatusCode c) ResponsePayload PayloadSchema ->
207โˆ’ "๐Ÿ“ฑโฌ…๏ธ JSON Response โ€“ " <> str (T.pack . show $ c) :: Inlines
258+ "โฌ…๏ธโ˜๏ธ JSON Response โ€“ " <> str (T.pack . show $ c) :: Inlines
208โˆ’ , constructReportJet $ \InRequest InPayload PayloadSchema -> "๐Ÿ“ฑโžก๏ธ JSON Request" :: Inlines
259+ , constructReportJet $ \InRequest InPayload PayloadSchema -> "โžก๏ธโ˜๏ธ JSON Request" :: Inlines
209 ]
260 ]
210 where
261 where
211 unwrapReportJetResult :: ReportJetResult Behave x -> ReportJet' Behave x
262 unwrapReportJetResult :: ReportJetResult Behave x -> ReportJet' Behave x
212 unwrapReportJetResult (Pure _) = error "There really shouldn't be any results here."
263 unwrapReportJetResult (Pure _) = error "There really shouldn't be any results here."
213 unwrapReportJetResult (Free f) = f
264 unwrapReportJetResult (Free f) = f
214
265
215โˆ’ jsonPathJet
266+ jsonPathJet ::
216โˆ’ :: NonEmpty
267+ NonEmpty
217โˆ’ ( Union
268+ ( Union
218โˆ’ '[ Behave 'SchemaLevel 'TypedSchemaLevel
269+ '[ Behave 'SchemaLevel 'TypedSchemaLevel
219โˆ’ , Behave 'TypedSchemaLevel 'SchemaLevel
270+ , Behave 'TypedSchemaLevel 'SchemaLevel
220โˆ’ ]
271+ ]
221โˆ’ )
272+ ) ->
222โˆ’ -> Inlines
273+ Inlines
223 jsonPathJet x = code $ "$" <> showParts (NE.toList x)
274 jsonPathJet x = code $ "$" <> showParts (NE.toList x)
224 where
275 where
225โˆ’ showParts
276+ showParts ::
226โˆ’ :: [ Union
277+ [ Union
227โˆ’ '[ Behave 'SchemaLevel 'TypedSchemaLevel
278+ '[ Behave 'SchemaLevel 'TypedSchemaLevel
228โˆ’ , Behave 'TypedSchemaLevel 'SchemaLevel
279+ , Behave 'TypedSchemaLevel 'SchemaLevel
229โˆ’ ]
280+ ]
230โˆ’ ]
281+ ] ->
231โˆ’ -> Text
282+ Text
232 showParts [] = mempty
283 showParts [] = mempty
233 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion (InProperty _)) : _)) = showParts xs
284 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion (InProperty _)) : _)) = showParts xs
234 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion InAdditionalProperty) : _)) = showParts xs
285 showParts (SingletonUnion (OfType Object) : xs@((SingletonUnion InAdditionalProperty) : _)) = showParts xs
235 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion InItems : _)) = showParts xs
286 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion InItems : _)) = showParts xs
236 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion (InItem _) : _)) = showParts xs
287 showParts (SingletonUnion (OfType Array) : xs@(SingletonUnion (InItem _) : _)) = showParts xs
237 showParts (y : ys) =
288 showParts (y : ys) =
238โˆ’ ((\(OfType t) -> "(" <> describeJSONType t <> ")")
289+ ( (\(OfType t) -> "(" <> describeJSONType t <> ")")
239โˆ’ @@> (\case
290+ @@> ( \case
240โˆ’ InItems -> "[*]"
291+ InItems -> "[*]"
241โˆ’ InItem i -> "[" <> T.pack (show i) <> "]"
292+ InItem i -> "[" <> T.pack (show i) <> "]"
242โˆ’ InProperty p -> "." <> p
293+ InProperty p -> "." <> p
243โˆ’ InAdditionalProperty -> ".*")
294+ InAdditionalProperty -> ".*"
244โˆ’ @@> typesExhausted)
295+ )
296+ @@> typesExhausted
297+ )
245 y
298 y
246 <> showParts ys
299 <> showParts ys
247
300
248โˆ’observeJetShowErrs
301+observeJetShowErrs ::
249โˆ’ :: ReportJet' Behave (Maybe Inlines)
302+ ReportJet' Behave (Maybe Inlines) ->
250โˆ’ -> P.PathsPrefixTree Behave AnIssue a
303+ P.PathsPrefixTree Behave AnIssue a ->
251โˆ’ -> (Report, P.PathsPrefixTree Behave AnIssue a)
304+ (Report, P.PathsPrefixTree Behave AnIssue a)
252observeJetShowErrs jet p = case observeJetShowErrs' jet p of
305observeJetShowErrs jet p = case observeJetShowErrs' jet p of
253 Just m -> m
306 Just m -> m
254 Nothing -> (mempty, p)
307 Nothing -> (mempty, p)
255
308
256โˆ’observeJetShowErrs'
309+observeJetShowErrs' ::
257โˆ’ :: forall a.
310+ forall a.
258โˆ’ ReportJet' Behave (Maybe Inlines)
311+ ReportJet' Behave (Maybe Inlines) ->
259โˆ’ -> P.PathsPrefixTree Behave AnIssue a
312+ P.PathsPrefixTree Behave AnIssue a ->
260โˆ’ -> Maybe (Report, P.PathsPrefixTree Behave AnIssue a)
313+ Maybe (Report, P.PathsPrefixTree Behave AnIssue a)
261observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues) =
314observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues) =
262 let results =
315 let results =
263 subIssues >>= \(WrapTypeable (AStep m)) ->
316 subIssues >>= \(WrapTypeable (AStep m)) ->
@@ -265,21 +318,23 @@ observeJetShowErrs' (ReportJet jet) (P.PathsPrefixNode currentIssues subIssues)
265 maybe (Left $ embed (step bhv) subErrs) Right . listToMaybe $
318 maybe (Left $ embed (step bhv) subErrs) Right . listToMaybe $
266 jet @_ @_ @[] bhv
319 jet @_ @_ @[] bhv
267 & mapMaybe
320 & mapMaybe
268โˆ’ (\case
321+ ( \case
269โˆ’ Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
322+ Free jet' -> fmap (embed $ step bhv) <$> observeJetShowErrs' jet' subErrs
270โˆ’ Pure (Just h) ->
323+ Pure (Just h) ->
271โˆ’ if P.null subErrs
324+ if P.null subErrs
272โˆ’ then Just mempty
325+ then Just mempty
273โˆ’ else Just (singletonHeader h (showErrs subErrs), mempty)
326+ else Just (singletonHeader h (showErrs subErrs), mempty)
274โˆ’ Pure Nothing -> Nothing)
327+ Pure Nothing -> Nothing
328+ )
275 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
329 in (fmap . fmap) (PathsPrefixNode currentIssues mempty <>) $
276 if any isRight results
330 if any isRight results
277 then
331 then
278 Just $
332 Just $
279 foldMap
333 foldMap
280โˆ’ (\case
334+ ( \case
281โˆ’ Left e -> (mempty, e)
335+ Left e -> (mempty, e)
282โˆ’ Right m -> m)
336+ Right m -> m
337+ )
283 results
338 results
284 else Nothing
339 else Nothing
285
340
@@ -302,8 +357,9 @@ buildReport cfg = case treeStyle cfg of
302 body rprt
357 body rprt
303 <> foldOMapWithKey
358 <> foldOMapWithKey
304 (headers rprt)
359 (headers rprt)
305โˆ’ (\k v ->
360+ ( \k v ->
306โˆ’ header level k <> subBuilder v)
361+ header level k <> subBuilder v
362+ )
307 where
363 where
308 subBuilder = headerStyleBuilder (level + 1)
364 subBuilder = headerStyleBuilder (level + 1)
309
365
@@ -312,16 +368,17 @@ buildReport cfg = case treeStyle cfg of
312 body rprt
368 body rprt
313 <> foldOMapWithKey
369 <> foldOMapWithKey
314 (headers rprt)
370 (headers rprt)
315โˆ’ (\k v ->
371+ ( \k v ->
316โˆ’ if (OM.size . headers $ rprt) < 2
372+ if (OM.size . headers $ rprt) < 2
317โˆ’ then para k <> blockQuote (subBuilder v)
373+ then para k <> blockQuote (subBuilder v)
318โˆ’ else
374+ else
319โˆ’ rawHtml "<details>"
375+ rawHtml "<details>"
320โˆ’ <> rawHtml "<summary>"
376+ <> rawHtml "<summary>"
321โˆ’ <> plain k
377+ <> plain k
322โˆ’ <> rawHtml "</summary>"
378+ <> rawHtml "</summary>"
323โˆ’ <> blockQuote (subBuilder v)
379+ <> blockQuote (subBuilder v)
324โˆ’ <> rawHtml "</details>")
380+ <> rawHtml "</details>"
381+ )
325 where
382 where
326 subBuilder = foldingStyleBuilder
383 subBuilder = foldingStyleBuilder
327
384
src/OpenAPI/Checker/Report/Html/Template.hs โ†’ src/Data/OpenApi/Compare/Report/Html/Template.hsrenamed+4โˆ’4
@@ -1,7 +1,7 @@
1{-# LANGUAGE TemplateHaskell #-}
1{-# LANGUAGE TemplateHaskell #-}
2
2
3โˆ’module OpenAPI.Checker.Report.Html.Template
3+module Data.OpenApi.Compare.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=\"OpenApi Diff\" />\
27+ \<meta name=\"generator\" content=\"CompaREST\" />\
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>โ›„ OpenApi Diff</h1></header>\
31+ \<header><h1>CompaREST</h1></header>\
32 \<main>\
32 \<main>\
33 \$body$\
33 \$body$\
34 \</main>\
34 \</main>\
src/OpenAPI/Checker/Report/Jet.hs โ†’ src/Data/OpenApi/Compare/Report/Jet.hsrenamed+6โˆ’6
@@ -1,8 +1,8 @@
1โˆ’module OpenAPI.Checker.Report.Jet
1+module Data.OpenApi.Compare.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 'Behaviour' tree paths.
26+-- The pattern fits well for simplifying 'Behavior' 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/Data/OpenApi/Compare/Run.hsnew file+31
@@ -0,0 +1,31 @@
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/OpenAPI/Checker/Subtree.hs โ†’ src/Data/OpenApi/Compare/Subtree.hsrenamed+128โˆ’126
@@ -1,52 +1,52 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Subtree
3+module Data.OpenApi.Compare.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
63import qualified Data.Set as S
68import qualified Data.Set as S
64import Data.Typeable
69import 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,25 +266,26 @@ 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+ )
282 else structuralIssue
283 else structuralIssue
283{-# INLINE iohmStructuralWith #-}
284{-# INLINE iohmStructuralWith #-}
284
285
285โˆ’runCompatFormula
286+runCompatFormula ::
286โˆ’ :: CompatFormula' q f r a
287+ CompatFormula' q f r a ->
287โˆ’ -> Either (P.PathsPrefixTree q f r) a
288+ Either (P.PathsPrefixTree q f r) a
288runCompatFormula (Compose f) =
289runCompatFormula (Compose f) =
289 calculate . runIdentity . runMemo 0 . unCompatM $ f
290 calculate . runIdentity . runMemo 0 . unCompatM $ f
290{-# INLINE runCompatFormula #-}
291{-# INLINE runCompatFormula #-}
@@ -314,27 +315,27 @@ invertIssueOrientationP = P.map (\(AnIssue ori i) -> AnIssue (toggleOrientation
314structuralIssue :: StructuralCompatFormula a
315structuralIssue :: StructuralCompatFormula a
315structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
316structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
316
317
317โˆ’anyOfAt
318+anyOfAt ::
318โˆ’ :: Issuable l
319+ Issuable l =>
319โˆ’ => Paths q r l
320+ Paths q r l ->
320โˆ’ -> Issue l
321+ Issue l ->
321โˆ’ -> [CompatFormula' q AnIssue r a]
322+ [CompatFormula' q AnIssue r a] ->
322โˆ’ -> CompatFormula' q AnIssue r a
323+ CompatFormula' q AnIssue r a
323anyOfAt _ _ [x] = x
324anyOfAt _ _ [x] = x
324anyOfAt xs issue fs =
325anyOfAt xs issue fs =
325 Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> traverse getCompose fs
326 Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> traverse getCompose fs
326
327
327-- | If the given formula contains any issues, add another issue on top. Otherwise succeed.
328-- | If the given formula contains any issues, add another issue on top. Otherwise succeed.
328โˆ’clarifyIssue
329+clarifyIssue ::
329โˆ’ :: AnItem q AnIssue r
330+ AnItem q AnIssue r ->
330โˆ’ -> CompatFormula' q AnIssue r a
331+ CompatFormula' q AnIssue r a ->
331โˆ’ -> CompatFormula' q AnIssue r a
332+ CompatFormula' q AnIssue r a
332clarifyIssue item f =
333clarifyIssue item f =
333 Compose ((`eitherOf` item) <$> pure <$> getCompose f) *> f
334 Compose ((`eitherOf` item) <$> pure <$> getCompose f) *> f
334
335
335โˆ’fixpointKnot
336+fixpointKnot ::
336โˆ’ :: MonadState (MemoState VarRef) m
337+ MonadState (MemoState VarRef) m =>
337โˆ’ => KnotTier (FormulaF q f r ()) VarRef m
338+ KnotTier (FormulaF q f r ()) VarRef m
338fixpointKnot =
339fixpointKnot =
339 KnotTier
340 KnotTier
340 { onKnotFound = modifyMemoNonce succ
341 { onKnotFound = modifyMemoNonce succ
@@ -342,19 +343,20 @@ fixpointKnot =
342 , tieKnot = \i x -> pure $ maxFixpoint i x
343 , tieKnot = \i x -> pure $ maxFixpoint i x
343 }
344 }
344
345
345โˆ’memo
346+memo ::
346โˆ’ :: (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a)
347+ (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a) =>
347โˆ’ => Paths q r l
348+ Paths q r l ->
348โˆ’ -> MemoKey
349+ MemoKey ->
349โˆ’ -> (ProdCons (Traced a) -> CompatFormula' q f r ())
350+ (ProdCons (Traced a) -> CompatFormula' q f r ()) ->
350โˆ’ -> (ProdCons (Traced a) -> CompatFormula' q f r ())
351+ (ProdCons (Traced a) -> CompatFormula' q f r ())
351memo bhv k f pc = Compose $ do
352memo bhv k f pc = Compose $ do
352 formula' <-
353 formula' <-
353 memoWithKnot
354 memoWithKnot
354 fixpointKnot
355 fixpointKnot
355โˆ’ (do
356+ ( do
356โˆ’ formula <- getCompose $ f pc
357+ formula <- getCompose $ f pc
357โˆ’ pure $ mapErrors (P.takeSubtree bhv) formula)
358+ pure $ mapErrors (P.takeSubtree bhv) formula
359+ )
358 (k, ask <$> pc)
360 (k, ask <$> pc)
359 pure $ mapErrors (P.embed bhv) formula'
361 pure $ mapErrors (P.embed bhv) formula'
360
362
src/OpenAPI/Checker/Validate/Header.hs โ†’ src/Data/OpenApi/Compare/Validate/Header.hsrenamed+9โˆ’7
@@ -1,6 +1,6 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.Header
3+module Data.OpenApi.Compare.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 OpenAPI.Checker.Behavior
12+import Data.OpenApi.Compare.Behavior
13โˆ’import OpenAPI.Checker.References ()
13+import Data.OpenApi.Compare.References ()
14โˆ’import OpenAPI.Checker.Subtree
14+import Data.OpenApi.Compare.Subtree
15โˆ’import OpenAPI.Checker.Validate.Schema ()
15+import Data.OpenApi.Compare.Validate.Schema ()
16import Text.Pandoc.Builder
16import Text.Pandoc.Builder
17
17
18instance Subtree Header where
18instance Subtree Header where
@@ -50,7 +50,9 @@ 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โˆ’ issueIsUnsupported _ = False
53+ issueKind = \case
54+ HeaderSchemaRequired -> ProbablyIssue -- catch-all schema?
55+ _ -> CertainIssue
54 describeIssue Forward RequiredHeaderMissing = para "Header has become required."
56 describeIssue Forward RequiredHeaderMissing = para "Header has become required."
55 describeIssue Backward RequiredHeaderMissing = para "Header is no longer required."
57 describeIssue Backward RequiredHeaderMissing = para "Header is no longer required."
56 describeIssue Forward NonEmptyHeaderRequired = para "The header does not allow empty values anymore."
58 describeIssue Forward NonEmptyHeaderRequired = para "The header does not allow empty values anymore."
@@ -61,4 +63,4 @@ instance Behavable 'HeaderLevel 'SchemaLevel where
61 data Behave 'HeaderLevel 'SchemaLevel
63 data Behave 'HeaderLevel 'SchemaLevel
62 = InSchema
64 = InSchema
63 deriving stock (Eq, Ord, Show)
65 deriving stock (Eq, Ord, Show)
64โˆ’ describeBehaviour InSchema = "JSON Schema"
66+ describeBehavior InSchema = "JSON Schema"
src/OpenAPI/Checker/Validate/Link.hs โ†’ src/Data/OpenApi/Compare/Validate/Link.hsrenamed+6โˆ’6
@@ -1,10 +1,10 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.Link () where
3+module Data.OpenApi.Compare.Validate.Link () where
4
4
5import Data.OpenApi
5import Data.OpenApi
6โˆ’import OpenAPI.Checker.Behavior
6+import Data.OpenApi.Compare.Behavior
7โˆ’import OpenAPI.Checker.Subtree
7+import Data.OpenApi.Compare.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โˆ’ issueIsUnsupported = \case
20+ issueKind = \case
21โˆ’ LinksUnsupported -> True
21+ LinksUnsupported -> Unsupported
22โˆ’ describeIssue _ LinksUnsupported = para "OpenApi Diff does not currently support Link Objects."
22+ describeIssue _ LinksUnsupported = para "CompaREST does not currently support Link Objects."
src/OpenAPI/Checker/Validate/MediaTypeObject.hs โ†’ src/Data/OpenApi/Compare/Validate/MediaTypeObject.hsrenamed+17โˆ’17
@@ -1,8 +1,8 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.MediaTypeObject
3+module Data.OpenApi.Compare.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 ()
15import Data.String
20import Data.String
16import Data.Text (Text)
21import Data.Text (Text)
17import qualified Data.Text as T
22import qualified Data.Text as T
18import Network.HTTP.Media (MediaType, mainType, subType)
23import 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โˆ’ issueIsUnsupported = \case
41+ issueKind = \case
42โˆ’ EncodingNotSupported -> True
42+ EncodingNotSupported -> Unsupported
43โˆ’ _ -> False
43+ _ -> CertainIssue
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 "OpenApi Diff does not currently support media encodings other than JSON."
48+ describeIssue _ EncodingNotSupported = para "CompaREST 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โˆ’ describeBehaviour PayloadSchema = "JSON Schema"
54+ describeBehavior 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โˆ’ -- https://github.com/typeable/openapi-diff/issues/54
122+ -- #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โˆ’ describeBehaviour (WithStatusCode c) = "Response code " <> (fromString . show $ c)
146+ describeBehavior (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โˆ’ issueIsUnsupported = \case
155+ issueKind = \case
156โˆ’ _ -> False
156+ _ -> CertainIssue
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/OpenAPI/Checker/Validate/OAuth2Flows.hs โ†’ src/Data/OpenApi/Compare/Validate/OAuth2Flows.hsrenamed+26โˆ’21
@@ -1,9 +1,9 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.OAuth2Flows
3+module Data.OpenApi.Compare.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
15import Data.Proxy
18import Data.Proxy
16import Data.Set (Set)
19import Data.Set (Set)
17import qualified Data.Set as S
20import qualified Data.Set as S
18import Data.Text (Text)
21import 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,7 +103,9 @@ 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โˆ’ issueIsUnsupported _ = False
106+ issueKind = \case
107+ SecurityRequirementNotMet -> CertainIssue
108+ UndefinedSecurityScheme _ -> SchemaInvalid
107 describeIssue Forward SecurityRequirementNotMet = para "Security scheme has been removed."
109 describeIssue Forward SecurityRequirementNotMet = para "Security scheme has been removed."
108 describeIssue Backward SecurityRequirementNotMet = para "Security scheme was added."
110 describeIssue Backward SecurityRequirementNotMet = para "Security scheme was added."
109 describeIssue _ (UndefinedSecurityScheme k) = para $ "Security scheme " <> code k <> " is not defined."
111 describeIssue _ (UndefinedSecurityScheme k) = para $ "Security scheme " <> code k <> " is not defined."
@@ -129,11 +131,14 @@ instance Issuable 'SecuritySchemeLevel where
129 | CanNotHaveScopes
131 | CanNotHaveScopes
130 | ScopeNotDefined Text
132 | ScopeNotDefined Text
131 deriving stock (Eq, Ord, Show)
133 deriving stock (Eq, Ord, Show)
132โˆ’ issueIsUnsupported _ = False
134+ issueKind = \case
135+ CanNotHaveScopes -> SchemaInvalid
136+ ScopeNotDefined _ -> SchemaInvalid
137+ _ -> CertainIssue
133 describeIssue _ RefreshUrlsDontMatch = para "Refresh URL changed."
138 describeIssue _ RefreshUrlsDontMatch = para "Refresh URL changed."
134 describeIssue _ (HttpSchemeTypesDontMatch _ _) = para "HTTP scheme type changed."
139 describeIssue _ (HttpSchemeTypesDontMatch _ _) = para "HTTP scheme type changed."
135 describeIssue _ (ApiKeyParamsDontMatch _ _) = para "API Key parameters changed."
140 describeIssue _ (ApiKeyParamsDontMatch _ _) = para "API Key parameters changed."
136โˆ’ describeIssue _ (OpenIdConnectUrlsDontMatch _ _) = para "OpenaId Connect URL changed."
141+ describeIssue _ (OpenIdConnectUrlsDontMatch _ _) = para "OpenID Connect URL changed."
137 describeIssue _ (CustomHttpSchemesDontMatch e a) =
142 describeIssue _ (CustomHttpSchemesDontMatch e a) =
138 para $ "Changed HTTP scheme from " <> code e <> " to " <> code a <> "."
143 para $ "Changed HTTP scheme from " <> code e <> " to " <> code a <> "."
139 describeIssue Forward ConsumerDoesNotSupportImplicitFlow = para "Implicit flow support has been removed."
144 describeIssue Forward ConsumerDoesNotSupportImplicitFlow = para "Implicit flow support has been removed."
@@ -162,4 +167,4 @@ instance Behavable 'SecurityRequirementLevel 'SecuritySchemeLevel where
162 data Behave 'SecurityRequirementLevel 'SecuritySchemeLevel
167 data Behave 'SecurityRequirementLevel 'SecuritySchemeLevel
163 = SecuritySchemeStep Text
168 = SecuritySchemeStep Text
164 deriving stock (Eq, Ord, Show)
169 deriving stock (Eq, Ord, Show)
165โˆ’ describeBehaviour (SecuritySchemeStep s) = text s
170+ describeBehavior (SecuritySchemeStep s) = text s
src/OpenAPI/Checker/Validate/OpenApi.hs โ†’ src/Data/OpenApi/Compare/Validate/OpenApi.hsrenamed+18โˆ’17
@@ -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 OpenAPI.Checker.Validate.OpenApi
5+module Data.OpenApi.Compare.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 OpenAPI.Checker.Behavior
14+import Data.OpenApi.Compare.Behavior
15โˆ’import OpenAPI.Checker.Paths
15+import Data.OpenApi.Compare.Paths
16โˆ’import OpenAPI.Checker.Subtree
16+import Data.OpenApi.Compare.Subtree
17โˆ’import OpenAPI.Checker.Validate.Operation
17+import Data.OpenApi.Compare.Validate.Operation
18
18
19tracedPaths :: Traced OpenApi -> Traced ProcessedPathItems
19tracedPaths :: Traced OpenApi -> Traced ProcessedPathItems
20tracedPaths oa =
20tracedPaths oa =
@@ -79,16 +79,17 @@ instance Subtree OpenApi where
79 checkSemanticCompatibility _ beh prodCons = do
79 checkSemanticCompatibility _ beh prodCons = do
80 checkCompatibility @ProcessedPathItems
80 checkCompatibility @ProcessedPathItems
81 beh
81 beh
82โˆ’ ((tracedRequestBodies <$> prodCons)
82+ ( (tracedRequestBodies <$> prodCons)
83โˆ’ `HCons` (tracedParameters <$> prodCons)
83+ `HCons` (tracedParameters <$> prodCons)
84โˆ’ `HCons` (tracedSecuritySchemes <$> prodCons)
84+ `HCons` (tracedSecuritySchemes <$> prodCons)
85โˆ’ `HCons` (tracedResponses <$> prodCons)
85+ `HCons` (tracedResponses <$> prodCons)
86โˆ’ `HCons` (tracedHeaders <$> prodCons)
86+ `HCons` (tracedHeaders <$> prodCons)
87โˆ’ `HCons` (tracedSchemas <$> prodCons)
87+ `HCons` (tracedSchemas <$> prodCons)
88โˆ’ `HCons` (_openApiServers . extract <$> prodCons)
88+ `HCons` (_openApiServers . extract <$> prodCons)
89โˆ’ `HCons` (tracedLinks <$> prodCons)
89+ `HCons` (tracedLinks <$> prodCons)
90โˆ’ `HCons` (tracedCallbacks <$> prodCons)
90+ `HCons` (tracedCallbacks <$> prodCons)
91โˆ’ `HCons` HNil)
91+ `HCons` HNil
92+ )
92 (tracedPaths <$> prodCons)
93 (tracedPaths <$> prodCons)
93
94
94instance Steppable OpenApi ProcessedPathItems where
95instance Steppable OpenApi ProcessedPathItems where
src/OpenAPI/Checker/Validate/Operation.hs โ†’ src/Data/OpenApi/Compare/Validate/Operation.hsrenamed+57โˆ’50
@@ -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 OpenAPI.Checker.Validate.Operation
5+module Data.OpenApi.Compare.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
32import Data.String
44import Data.String
33import Data.Text (Text)
45import Data.Text (Text)
34import qualified Data.Text as T
46import 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,11 +86,12 @@ 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: https://github.com/typeable/openapi-diff/issues/28
89+-- FIXME: #28
90โˆ’getServers
90+getServers ::
91โˆ’ :: [Server] -- ^ Servers from env
91+ -- | Servers from env
92โˆ’ -> MatchedOperation
92+ [Server] ->
93โˆ’ -> [Server]
93+ MatchedOperation ->
94+ [Server]
94getServers env oper =
95getServers env oper =
95 case _operationServers . operation $ oper of
96 case _operationServers . operation $ oper of
96 [] -> env
97 [] -> env
@@ -101,21 +102,21 @@ instance Behavable 'OperationLevel 'PathFragmentLevel where
101 = InParam Text
102 = InParam Text
102 | InFragment (PathFragment Text)
103 | InFragment (PathFragment Text)
103 deriving stock (Eq, Ord, Show)
104 deriving stock (Eq, Ord, Show)
104โˆ’ describeBehaviour (InParam p) = "Parameter " <> text p
105+ describeBehavior (InParam p) = "Parameter " <> text p
105โˆ’ describeBehaviour (InFragment (StaticPath p)) = "Static fragment " <> code p
106+ describeBehavior (InFragment (StaticPath p)) = "Static fragment " <> code p
106โˆ’ describeBehaviour (InFragment (DynamicPath p)) = "Dynamic fragment " <> code p
107+ describeBehavior (InFragment (DynamicPath p)) = "Dynamic fragment " <> code p
107
108
108instance Behavable 'OperationLevel 'RequestLevel where
109instance Behavable 'OperationLevel 'RequestLevel where
109 data Behave 'OperationLevel 'RequestLevel
110 data Behave 'OperationLevel 'RequestLevel
110 = InRequest
111 = InRequest
111 deriving stock (Eq, Ord, Show)
112 deriving stock (Eq, Ord, Show)
112โˆ’ describeBehaviour InRequest = "Request"
113+ describeBehavior InRequest = "Request"
113
114
114instance Behavable 'OperationLevel 'SecurityRequirementLevel where
115instance Behavable 'OperationLevel 'SecurityRequirementLevel where
115 data Behave 'OperationLevel 'SecurityRequirementLevel
116 data Behave 'OperationLevel 'SecurityRequirementLevel
116 = SecurityRequirementStep Int
117 = SecurityRequirementStep Int
117 deriving stock (Eq, Ord, Show)
118 deriving stock (Eq, Ord, Show)
118โˆ’ describeBehaviour (SecurityRequirementStep i) =
119+ describeBehavior (SecurityRequirementStep i) =
119 "Security requirement " <> (text . T.pack . show $ i)
120 "Security requirement " <> (text . T.pack . show $ i)
120
121
121instance Subtree MatchedOperation where
122instance Subtree MatchedOperation where
@@ -241,7 +242,7 @@ instance Subtree MatchedOperation where
241 checkProducts beh (const NoRequestBody) (const check) elements
242 checkProducts beh (const NoRequestBody) (const check) elements
242 checkResponses =
243 checkResponses =
243 swapProdCons (checkCompatibility beh) env $ tracedResponses <$> prodCons
244 swapProdCons (checkCompatibility beh) env $ tracedResponses <$> prodCons
244โˆ’ -- FIXME: https://github.com/typeable/openapi-diff/issues/27
245+ -- FIXME: #27
245 checkCallbacks = do
246 checkCallbacks = do
246 let ProdCons pCallbacks cCallbacks = tracedCallbacks <$> prodCons
247 let ProdCons pCallbacks cCallbacks = tracedCallbacks <$> prodCons
247 for_ pCallbacks $ \(k, pCallback) -> do
248 for_ pCallbacks $ \(k, pCallback) -> do
@@ -250,7 +251,7 @@ instance Subtree MatchedOperation where
250 cCallbacks <&> \(_, cCallback) -> do
251 cCallbacks <&> \(_, cCallback) -> do
251 swapProdCons (checkCompatibility beh') env $ ProdCons pCallback cCallback
252 swapProdCons (checkCompatibility beh') env $ ProdCons pCallback cCallback
252 pure ()
253 pure ()
253โˆ’ -- FIXME: https://github.com/typeable/openapi-diff/issues/28
254+ -- FIXME: #28
254 checkOperationSecurity = do
255 checkOperationSecurity = do
255 let ProdCons pSecs cSecs = tracedSecurity <$> prodCons
256 let ProdCons pSecs cSecs = tracedSecurity <$> prodCons
256 for_ pSecs $ \(i, pSec) -> do
257 for_ pSecs $ \(i, pSec) -> do
@@ -335,7 +336,12 @@ instance Issuable 'APILevel where
335 | AllPathsFailed FilePath
336 | AllPathsFailed FilePath
336 -- When several paths match given but all checks failed
337 -- When several paths match given but all checks failed
337 deriving stock (Eq, Ord, Show)
338 deriving stock (Eq, Ord, Show)
338โˆ’ issueIsUnsupported _ = False
339+ issueKind = \case
340+ _ -> CertainIssue
341+ relatedIssues =
342+ (==) `withClass` \case
343+ NoPathsMatched fp -> Just fp
344+ AllPathsFailed fp -> Just fp
339 describeIssue Forward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
345 describeIssue Forward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
340 describeIssue Backward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been added."
346 describeIssue Backward (NoPathsMatched p) = para $ "The path " <> (code . T.pack) p <> " has been added."
341 describeIssue Forward (AllPathsFailed p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
347 describeIssue Forward (AllPathsFailed p) = para $ "The path " <> (code . T.pack) p <> " has been removed."
@@ -346,7 +352,7 @@ instance Behavable 'APILevel 'PathLevel where
346 = AtPath FilePath
352 = AtPath FilePath
347 deriving stock (Eq, Ord, Show)
353 deriving stock (Eq, Ord, Show)
348
354
349โˆ’ describeBehaviour (AtPath p) = str (T.pack p)
355+ describeBehavior (AtPath p) = str (T.pack p)
350
356
351instance Subtree ProcessedPathItems where
357instance Subtree ProcessedPathItems where
352 type SubtreeLevel ProcessedPathItems = 'APILevel
358 type SubtreeLevel ProcessedPathItems = 'APILevel
@@ -430,17 +436,18 @@ tracedFragments mpi =
430 | (i, x) <- L.zip [0 ..] $ pathFragments $ extract mpi
436 | (i, x) <- L.zip [0 ..] $ pathFragments $ extract mpi
431 ]
437 ]
432
438
433โˆ’tracedMethod
439+tracedMethod ::
434โˆ’ :: OperationMethod
440+ OperationMethod ->
435โˆ’ -> Traced MatchedPathItem
441+ Traced MatchedPathItem ->
436โˆ’ -> Maybe (Traced' MatchedOperation Operation)
442+ Maybe (Traced' MatchedOperation Operation)
437tracedMethod s mpi = env (ask mpi >>> step (OperationMethodStep s)) <$> (pathItemMethod s . pathItem . extract $ mpi)
443tracedMethod s mpi = env (ask mpi >>> step (OperationMethodStep s)) <$> (pathItemMethod s . pathItem . extract $ mpi)
438
444
439instance Issuable 'PathLevel where
445instance Issuable 'PathLevel where
440 data Issue 'PathLevel
446 data Issue 'PathLevel
441 = OperationMissing OperationMethod
447 = OperationMissing OperationMethod
442 deriving stock (Eq, Ord, Show)
448 deriving stock (Eq, Ord, Show)
443โˆ’ issueIsUnsupported _ = False
449+ issueKind = \case
450+ OperationMissing _ -> CertainIssue
444 describeIssue Forward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been removed."
451 describeIssue Forward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been removed."
445 describeIssue Backward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been added."
452 describeIssue Backward (OperationMissing op) = para $ "Method " <> strong (showMethod op) <> " has been added."
446
453
@@ -449,7 +456,7 @@ instance Behavable 'PathLevel 'OperationLevel where
449 = InOperation OperationMethod
456 = InOperation OperationMethod
450 deriving stock (Eq, Ord, Show)
457 deriving stock (Eq, Ord, Show)
451
458
452โˆ’ describeBehaviour (InOperation method) = showMethod method
459+ describeBehavior (InOperation method) = showMethod method
453
460
454showMethod :: IsString s => OperationMethod -> s
461showMethod :: IsString s => OperationMethod -> s
455showMethod = \case
462showMethod = \case
@@ -480,10 +487,10 @@ instance Subtree MatchedPathItem where
480 checkSemanticCompatibility env beh prodCons = do
487 checkSemanticCompatibility env beh prodCons = do
481 let paramDefs = getH @(ProdCons (Traced (Definitions Param))) env
488 let paramDefs = getH @(ProdCons (Traced (Definitions Param))) env
482 pathTracedParams = getPathParams <$> paramDefs <*> prodCons
489 pathTracedParams = getPathParams <$> paramDefs <*> prodCons
483โˆ’ getPathParams
490+ getPathParams ::
484โˆ’ :: Traced (Definitions Param)
491+ Traced (Definitions Param) ->
485โˆ’ -> Traced MatchedPathItem
492+ Traced MatchedPathItem ->
486โˆ’ -> [Traced Param]
493+ [Traced Param]
487 getPathParams defs mpi = do
494 getPathParams defs mpi = do
488 paramRef <- tracedMatchedPathItemParameters mpi
495 paramRef <- tracedMatchedPathItemParameters mpi
489 pure $ dereference defs paramRef
496 pure $ dereference defs paramRef
@@ -557,9 +564,9 @@ instance Issuable 'CallbackLevel where
557 data Issue 'CallbackLevel
564 data Issue 'CallbackLevel
558 = CallbacksUnsupported
565 = CallbacksUnsupported
559 deriving stock (Eq, Ord, Show)
566 deriving stock (Eq, Ord, Show)
560โˆ’ issueIsUnsupported = \case
567+ issueKind = \case
561โˆ’ CallbacksUnsupported -> True
568+ CallbacksUnsupported -> Unsupported
562โˆ’ describeIssue _ CallbacksUnsupported = para "OpenApi Diff does not currently support callbacks."
569+ describeIssue _ CallbacksUnsupported = para "CompaREST does not currently support callbacks."
563
570
564tracedCallbackPathItems :: Traced Callback -> Traced ProcessedPathItems
571tracedCallbackPathItems :: Traced Callback -> Traced ProcessedPathItems
565tracedCallbackPathItems (Traced t (Callback x)) =
572tracedCallbackPathItems (Traced t (Callback x)) =
@@ -573,4 +580,4 @@ instance Behavable 'OperationLevel 'CallbackLevel where
573 data Behave 'OperationLevel 'CallbackLevel = OperationCallback Text
580 data Behave 'OperationLevel 'CallbackLevel = OperationCallback Text
574 deriving stock (Eq, Ord, Show)
581 deriving stock (Eq, Ord, Show)
575
582
576โˆ’ describeBehaviour (OperationCallback key) = "Operation " <> code key
583+ describeBehavior (OperationCallback key) = "Operation " <> code key
src/OpenAPI/Checker/Validate/Param.hs โ†’ src/Data/OpenApi/Compare/Validate/Param.hsrenamed+18โˆ’14
@@ -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 OpenAPI.Checker.Validate.Param
4+module Data.OpenApi.Compare.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 ()
14import Data.Text as T
18import 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,7 +68,9 @@ 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โˆ’ issueIsUnsupported _ = False
71+ issueKind = \case
72+ ParamSchemaMismatch -> ProbablyIssue -- the schema could be catch-all (?)
73+ _ -> CertainIssue
72 describeIssue _ ParamNameMismatch = para "The path fragments don't match."
74 describeIssue _ ParamNameMismatch = para "The path fragments don't match."
73 describeIssue Forward ParamEmptinessIncompatible = para "The parameter can no longer be empty."
75 describeIssue Forward ParamEmptinessIncompatible = para "The parameter can no longer be empty."
74 describeIssue Backward ParamEmptinessIncompatible = para "The parameter can now be empty."
76 describeIssue Backward ParamEmptinessIncompatible = para "The parameter can now be empty."
@@ -77,7 +79,7 @@ instance Issuable 'PathFragmentLevel where
77 describeIssue _ ParamPlaceIncompatible = para "Parameters in incompatible locations."
79 describeIssue _ ParamPlaceIncompatible = para "Parameters in incompatible locations."
78 describeIssue _ ParamStyleMismatch = para "Different parameter styles (encodings)."
80 describeIssue _ ParamStyleMismatch = para "Different parameter styles (encodings)."
79 describeIssue _ ParamSchemaMismatch = para "Expected a schema, but didn't find one."
81 describeIssue _ ParamSchemaMismatch = para "Expected a schema, but didn't find one."
80โˆ’ describeIssue ori (PathFragmentsDontMatch (orientProdCons ori -> ProdCons e a)) =
82+ describeIssue ori (PathFragmentsDontMatch (orientProdCons ori -> ProdCons e a)) =
81 para $ "Parameter changed from " <> code e <> " to " <> code a <> "."
83 para $ "Parameter changed from " <> code e <> " to " <> code a <> "."
82
84
83instance Behavable 'PathFragmentLevel 'SchemaLevel where
85instance Behavable 'PathFragmentLevel 'SchemaLevel where
@@ -85,7 +87,7 @@ instance Behavable 'PathFragmentLevel 'SchemaLevel where
85 = InParamSchema
87 = InParamSchema
86 deriving stock (Eq, Ord, Show)
88 deriving stock (Eq, Ord, Show)
87
89
88โˆ’ describeBehaviour InParamSchema = "JSON Schema"
90+ describeBehavior InParamSchema = "JSON Schema"
89
91
90instance Subtree Param where
92instance Subtree Param where
91 type SubtreeLevel Param = 'PathFragmentLevel
93 type SubtreeLevel Param = 'PathFragmentLevel
@@ -104,15 +106,17 @@ instance Subtree Param where
104 when (_paramName (extract p) /= _paramName (extract c)) $
106 when (_paramName (extract p) /= _paramName (extract c)) $
105 issueAt beh ParamNameMismatch
107 issueAt beh ParamNameMismatch
106 when
108 when
107โˆ’ ((fromMaybe False . _paramRequired . extract $ c)
109+ ( (fromMaybe False . _paramRequired . extract $ c)
108โˆ’ && not (fromMaybe False . _paramRequired . extract $ p))
110+ && not (fromMaybe False . _paramRequired . extract $ p)
111+ )
109 $ issueAt beh ParamRequired
112 $ issueAt beh ParamRequired
110 case (_paramIn . extract $ p, _paramIn . extract $ c) of
113 case (_paramIn . extract $ p, _paramIn . extract $ c) of
111 (ParamQuery, ParamQuery) -> do
114 (ParamQuery, ParamQuery) -> do
112 -- Emptiness is only for query params
115 -- Emptiness is only for query params
113 when
116 when
114โˆ’ ((fromMaybe False . _paramAllowEmptyValue . extract $ p)
117+ ( (fromMaybe False . _paramAllowEmptyValue . extract $ p)
115โˆ’ && not (fromMaybe False . _paramAllowEmptyValue . extract $ c))
118+ && not (fromMaybe False . _paramAllowEmptyValue . extract $ c)
119+ )
116 $ issueAt beh ParamEmptinessIncompatible
120 $ issueAt beh ParamEmptinessIncompatible
117 (a, b) | a == b -> pure ()
121 (a, b) | a == b -> pure ()
118 _ -> issueAt beh ParamPlaceIncompatible
122 _ -> issueAt beh ParamPlaceIncompatible
src/OpenAPI/Checker/Validate/PathFragment.hs โ†’ src/Data/OpenApi/Compare/Validate/PathFragment.hsrenamed+8โˆ’9
@@ -1,21 +1,20 @@
1โˆ’module OpenAPI.Checker.Validate.PathFragment
1+module Data.OpenApi.Compare.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
10import Data.Text (Text)
13import Data.Text (Text)
11import qualified Data.Text as T
14import 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
16
15
17-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
16-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
18โˆ’-- https://github.com/typeable/openapi-diff/issues/23
17+-- #23
19parsePath :: FilePath -> [PathFragment Text]
18parsePath :: FilePath -> [PathFragment Text]
20parsePath = fmap partition . T.splitOn "/" . T.pack
19parsePath = fmap partition . T.splitOn "/" . T.pack
21 where
20 where
src/Data/OpenApi/Compare/Validate/Products.hsnew file+48
@@ -0,0 +1,48 @@
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/OpenAPI/Checker/Validate/RequestBody.hs โ†’ src/Data/OpenApi/Compare/Validate/RequestBody.hsrenamed+10โˆ’9
@@ -1,8 +1,8 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.RequestBody
3+module Data.OpenApi.Compare.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
14import qualified Data.Text as T
18import qualified Data.Text as T
15import Network.HTTP.Media (MediaType)
19import 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,7 +30,8 @@ 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โˆ’ issueIsUnsupported _ = False
33+ issueKind = \case
34+ _ -> CertainIssue
34 describeIssue Forward RequestBodyRequired =
35 describeIssue Forward RequestBodyRequired =
35 para "Request body has become required."
36 para "Request body has become required."
36 describeIssue Backward RequestBodyRequired =
37 describeIssue Backward RequestBodyRequired =
@@ -44,7 +45,7 @@ instance Behavable 'RequestLevel 'PayloadLevel where
44 data Behave 'RequestLevel 'PayloadLevel
45 data Behave 'RequestLevel 'PayloadLevel
45 = InPayload
46 = InPayload
46 deriving stock (Eq, Ord, Show)
47 deriving stock (Eq, Ord, Show)
47โˆ’ describeBehaviour InPayload = "Payload"
48+ describeBehavior InPayload = "Payload"
48
49
49instance Subtree RequestBody where
50instance Subtree RequestBody where
50 type SubtreeLevel RequestBody = 'RequestLevel
51 type SubtreeLevel RequestBody = 'RequestLevel
src/OpenAPI/Checker/Validate/Responses.hs โ†’ src/Data/OpenApi/Compare/Validate/Responses.hsrenamed+15โˆ’14
@@ -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 OpenAPI.Checker.Validate.Responses
4+module Data.OpenApi.Compare.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
14import qualified Data.Text as T
23import qualified Data.Text as T
15import Network.HTTP.Media (MediaType)
24import 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,7 +71,8 @@ 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โˆ’ issueIsUnsupported _ = False
74+ issueKind = \case
75+ _ -> CertainIssue
75 describeIssue Forward (ConsumerDoesntHaveMediaType t) =
76 describeIssue Forward (ConsumerDoesntHaveMediaType t) =
76 para $ "Media type was removed: " <> (code . T.pack . show $ t) <> "."
77 para $ "Media type was removed: " <> (code . T.pack . show $ t) <> "."
77 describeIssue Backward (ConsumerDoesntHaveMediaType t) =
78 describeIssue Backward (ConsumerDoesntHaveMediaType t) =
@@ -86,14 +87,14 @@ instance Behavable 'ResponseLevel 'PayloadLevel where
86 = ResponsePayload
87 = ResponsePayload
87 deriving stock (Eq, Ord, Show)
88 deriving stock (Eq, Ord, Show)
88
89
89โˆ’ describeBehaviour ResponsePayload = "Payload"
90+ describeBehavior ResponsePayload = "Payload"
90
91
91instance Behavable 'ResponseLevel 'HeaderLevel where
92instance Behavable 'ResponseLevel 'HeaderLevel where
92 data Behave 'ResponseLevel 'HeaderLevel
93 data Behave 'ResponseLevel 'HeaderLevel
93 = InHeader HeaderName
94 = InHeader HeaderName
94 deriving stock (Eq, Ord, Show)
95 deriving stock (Eq, Ord, Show)
95
96
96โˆ’ describeBehaviour (InHeader name) = "Header " <> code name
97+ describeBehavior (InHeader name) = "Header " <> code name
97
98
98instance Subtree Response where
99instance Subtree Response where
99 type SubtreeLevel Response = 'ResponseLevel
100 type SubtreeLevel Response = 'ResponseLevel
src/OpenAPI/Checker/Validate/Schema.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema.hsrenamed+95โˆ’115
@@ -1,5 +1,6 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2โˆ’module OpenAPI.Checker.Validate.Schema
2+
3+module Data.OpenApi.Compare.Validate.Schema
3 (
4 (
4 )
5 )
5where
6where
@@ -10,63 +11,64 @@ import Data.Coerce
10import Data.Foldable (for_, toList)
11import Data.Foldable (for_, toList)
11import Data.Functor
12import Data.Functor
12import Data.HList
13import Data.HList
14+import Data.List (genericIndex, genericLength, group)
13import qualified Data.List.NonEmpty as NE
15import qualified Data.List.NonEmpty as NE
14import qualified Data.Map as M
16import qualified Data.Map as M
15โˆ’import Data.List (group, genericLength, genericIndex)
16import Data.Maybe
17import Data.Maybe
17import Data.OpenApi
18import 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
18import Data.Ord
30import Data.Ord
19import Data.Ratio
31import Data.Ratio
20import Data.Semigroup
32import Data.Semigroup
21import qualified Data.Set as S
33import qualified Data.Set as S
22import Data.Text (Text)
34import 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
34
35
35โˆ’checkFormulas
36+checkFormulas ::
36โˆ’ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
37+ (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
37โˆ’ => HList xs
38+ HList xs ->
38โˆ’ -> Behavior 'SchemaLevel
39+ Behavior 'SchemaLevel ->
39โˆ’ -> ProdCons (Trace Schema)
40+ ProdCons (Trace Schema) ->
40โˆ’ -> ProdCons (Traced (Definitions Schema))
41+ ProdCons (Traced (Definitions Schema)) ->
41โˆ’ -> ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
42+ ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel) ->
42โˆ’ -> SemanticCompatFormula ()
43+ SemanticCompatFormula ()
43checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
44checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
44 case P.toList ep ++ P.toList ec of
45 case P.toList ep ++ P.toList ec of
45 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
46 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
46 [] -> do
47 [] -> do
47 -- We have the following isomorphisms:
48 -- 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 โŠ‚ โˆ…) <= 0
54+ -- 1) (A โŠ‚ X โˆช Y) <= (A โŠ‚ X) \/ (A โŠ‚ Y)
55โˆ’ -- 2) (X โˆฉ Y โŠ‚ B) <= (X โŠ‚ B) \/ (Y โŠ‚ B)
55+ -- 2) (A โŠ‚ โˆ…) <= 0
56โˆ’ -- 3) (โŠค โŠ‚ B) <= 0
56+ -- 3) (X โˆฉ Y โŠ‚ B) <= (X โŠ‚ B) \/ (Y โŠ‚ B)
57โˆ’ -- Therefore we have the isomorphisms with (โˆƒ and โˆ€ being the N-ary
57+ -- 4) (โŠค โŠ‚ B) <= 0
58+ -- Therefore we have the implications with (โˆƒ and โˆ€ being the N-ary
58 -- versions of \/ and /\ respectively):
59 -- versions of \/ and /\ respectively):
59 -- (โ‹ƒ_i โ‹‚_j A[i,j]) โŠ‚ (โ‹ƒ_k โ‹‚_l B[k,l])
60 -- (โ‹ƒ_i โ‹‚_j A[i,j]) โŠ‚ (โ‹ƒ_k โ‹‚_l B[k,l])
60โˆ’ -- = โˆƒk โˆ€l โˆ€i, (โ‹‚_j A[i,j]) โŠ‚ B[k,l]
61+ -- <= โˆƒk โˆ€l โˆ€i, (โ‹‚_j A[i,j]) โŠ‚ B[k,l]
61 -- = โˆ€i โˆƒk โˆ€l, (โ‹‚_j A[i,j]) โŠ‚ B[k,l]
62 -- = โˆ€i โˆƒk โˆ€l, (โ‹‚_j A[i,j]) โŠ‚ B[k,l]
62 -- with the caveat that the the set over which k ranges is nonempty.
63 -- with the caveat that the the set over which k ranges is nonempty.
63โˆ’ -- This is because 1) is not an isomorphism.
64+ -- (because 2) is not an isomorphism), and that this is a sufficient
65+ -- but not necessary condition (because 1) is not an isomorphism).
64 -- Our disjunction loses information, so it makes sense to nest it as
66 -- Our disjunction loses information, so it makes sense to nest it as
65 -- deeply as possible, hence we choose the latter representation.
67 -- deeply as possible, hence we choose the latter representation.
66 --
68 --
67 -- We delegate the verification of (โ‹‚_j A[j]) โŠ‚ B to a separate heuristic
69 -- We delegate the verification of (โ‹‚_j A[j]) โŠ‚ B to a separate heuristic
68 -- function, with the understanding that โˆƒj, A[j] โŠ‚ B is a sufficient,
70 -- function, with the understanding that โˆƒj, A[j] โŠ‚ B is a sufficient,
69โˆ’ -- but not necessary condition (because of 2) and 3)).
71+ -- but not necessary condition (because of 3) and 4)).
70 --
72 --
71 -- If k ranges over an empty set, we have the isomorphism:
73 -- If k ranges over an empty set, we have the isomorphism:
72 -- (โ‹ƒ_i โ‹‚_j A[i,j]) โŠ‚ โˆ… = โˆ€i, (โ‹‚_j A[i,j]) โŠ‚ โˆ…
74 -- (โ‹ƒ_i โ‹‚_j A[i,j]) โŠ‚ โˆ… = โˆ€i, (โ‹‚_j A[i,j]) โŠ‚ โˆ…
@@ -143,21 +145,21 @@ checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
143 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
145 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
144 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
146 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
145
147
146โˆ’checkContradiction
148+checkContradiction ::
147โˆ’ :: Behavior 'TypedSchemaLevel
149+ Behavior 'TypedSchemaLevel ->
148โˆ’ -> Maybe Partition
150+ Maybe Partition ->
149โˆ’ -> S.Set (Condition t)
151+ S.Set (Condition t) ->
150โˆ’ -> SemanticCompatFormula ()
152+ SemanticCompatFormula ()
151checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
153checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
152
154
153โˆ’checkImplication
155+checkImplication ::
154โˆ’ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
156+ (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
155โˆ’ => HList xs
157+ HList xs ->
156โˆ’ -> Behavior 'TypedSchemaLevel
158+ Behavior 'TypedSchemaLevel ->
157โˆ’ -> ProdCons (Trace Schema) -- the traces of the root schemas used in this comparison
159+ ProdCons (Trace Schema) -> -- the traces of the root schemas used in this comparison
158โˆ’ -> S.Set (Condition t)
160+ S.Set (Condition t) ->
159โˆ’ -> Condition t
161+ Condition t ->
160โˆ’ -> SemanticCompatFormula ()
162+ SemanticCompatFormula ()
161checkImplication env beh trs prods cons = case findExactly prods of
163checkImplication env beh trs prods cons = case findExactly prods of
162 Just e
164 Just e
163 | all (satisfiesTyped e) prods ->
165 | all (satisfiesTyped e) prods ->
@@ -165,124 +167,104 @@ checkImplication env beh trs prods cons = case findExactly prods of
165 then pure ()
167 then pure ()
166 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
168 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
167 | otherwise -> pure () -- vacuously true
169 | otherwise -> pure () -- vacuously true
168โˆ’
169 Nothing -> case cons of
170 Nothing -> case cons of
170 -- the above code didn't catch it, so there's no Exactly condition on the lhs
171 -- the above code didn't catch it, so there's no Exactly condition on the lhs
171 Exactly e -> issueAt beh (NoMatchingEnum $ untypeValue e)
172 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โˆ’
177 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
176 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
178 Minimum m' -> Just m'
177 Minimum m' -> Just m'
179 _ -> Nothing
178 _ -> Nothing
180โˆ’
181 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
179 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
182 MultipleOf m' -> Just m'
180 MultipleOf m' -> Just m'
183 _ -> Nothing
181 _ -> Nothing
184โˆ’
185 NumberFormat f -> case flip any prods $ \case
182 NumberFormat f -> case flip any prods $ \case
186โˆ’ NumberFormat f' -> f == f'
183+ NumberFormat f' -> f == f'
187โˆ’ _ -> False
184+ _ -> False of
188โˆ’ of
189 True -> pure ()
185 True -> pure ()
190 False -> issueAt beh (NoMatchingFormat f)
186 False -> issueAt beh (NoMatchingFormat f)
191โˆ’
192 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
187 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
193 MaxLength m' -> Just m'
188 MaxLength m' -> Just m'
194 _ -> Nothing
189 _ -> Nothing
195โˆ’
196 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
190 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
197 MinLength m' -> Just m'
191 MinLength m' -> Just m'
198 _ -> Nothing
192 _ -> Nothing
199โˆ’
200 Pattern p -> case flip any prods $ \case
193 Pattern p -> case flip any prods $ \case
201โˆ’ Pattern p' -> p == p'
194+ Pattern p' -> p == p'
202โˆ’ _ -> False
195+ _ -> False of
203โˆ’ of
204 True -> pure ()
196 True -> pure ()
205 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
197 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
206โˆ’
207 StringFormat f -> case flip any prods $ \case
198 StringFormat f -> case flip any prods $ \case
208โˆ’ StringFormat f' -> f == f'
199+ StringFormat f' -> f == f'
209โˆ’ _ -> False
200+ _ -> False of
210โˆ’ of
211 True -> pure ()
201 True -> pure ()
212 False -> issueAt beh (NoMatchingFormat f)
202 False -> issueAt beh (NoMatchingFormat f)
213โˆ’
214 Items _ cons' -> case foldSome (<>) prods $ \case
203 Items _ cons' -> case foldSome (<>) prods $ \case
215โˆ’ Items _ rs -> Just (Just (rs NE.:| []), mempty)
204+ Items _ rs -> Just (Just (rs NE.:| []), mempty)
216โˆ’ TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
205+ TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
217โˆ’ _ -> Nothing
206+ _ -> Nothing of
218โˆ’ of
219 Just (mItems, Just pfs)
207 Just (mItems, Just pfs)
220 | not $ allSame (length <$> pfs) -> pure () -- vacuously
208 | not $ allSame (length <$> pfs) -> pure () -- vacuously
221โˆ’ | let plen = genericLength (NE.head pfs)
209+ | let plen = genericLength (NE.head pfs) ->
222โˆ’ -> clarifyIssue (AnItem beh (anIssue TupleToArray)) $ for_ [0 .. plen - 1] $ \i -> do
210+ clarifyIssue (AnItem beh (anIssue TupleToArray)) $
223โˆ’ let prod' = tracedConjunct $ case mItems of
211+ for_ [0 .. plen - 1] $ \i -> do
224โˆ’ Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
212+ let prod' = tracedConjunct $ case mItems of
225โˆ’ Nothing -> (`genericIndex` i) <$> pfs
213+ Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
226โˆ’ checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
214+ Nothing -> (`genericIndex` i) <$> pfs
215+ checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
227 Just (Just prods', Nothing) -> do
216 Just (Just prods', Nothing) -> do
228 let prod' = tracedConjunct prods'
217 let prod' = tracedConjunct prods'
229 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
218 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
230 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
219 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
231 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
220 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
232โˆ’
233 TupleItems (map snd -> fs) -> case foldSome (<>) prods $ \case
221 TupleItems (map snd -> fs) -> case foldSome (<>) prods $ \case
234โˆ’ TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
222+ TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
235โˆ’ MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
223+ MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
236โˆ’ MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
224+ MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
237โˆ’ Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
225+ Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
238โˆ’ _ -> Nothing
226+ _ -> Nothing of
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
248 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
235 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
249โˆ’ -- We have a fixed length array in the producer...
236+ -- We have a fixed length array in the producer...
250โˆ’ Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
237+ Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
251โˆ’ | plen == plen'
238+ | plen == plen' ->
252โˆ’ -> clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
239+ clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
253โˆ’ _ | plen /= genericLength fs -> -- ...of wrong length
240+ _
254โˆ’ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
241+ | plen /= genericLength fs -> -- ...of wrong length
242+ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
255 Just rs -> for_ [0 .. plen - 1] $ \i -> do
243 Just rs -> for_ [0 .. plen - 1] $ \i -> do
256 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
244 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
257 -- ...and no "items" schema
245 -- ...and no "items" schema
258 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
246 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
259 for_ [0 .. plen - 1] $ \i -> do
247 for_ [0 .. plen - 1] $ \i -> do
260 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
248 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
261โˆ’ _ -> issueAt beh NoMatchingTupleItems
249+ _ -> issueAt beh NoMatchingTupleItems
262โˆ’
263 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
250 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
264 MaxItems m' -> Just m'
251 MaxItems m' -> Just m'
265 TupleItems fs -> Just $ toInteger $ length fs
252 TupleItems fs -> Just $ toInteger $ length fs
266 _ -> Nothing
253 _ -> Nothing
267โˆ’
268 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
254 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
269 MinItems m' -> Just m'
255 MinItems m' -> Just m'
270 TupleItems fs -> Just $ toInteger $ length fs
256 TupleItems fs -> Just $ toInteger $ length fs
271 _ -> Nothing
257 _ -> Nothing
272โˆ’
273 UniqueItems -> case flip any prods $ \case
258 UniqueItems -> case flip any prods $ \case
274โˆ’ UniqueItems -> True
259+ UniqueItems -> True
275โˆ’ MaxItems 1 -> True
260+ MaxItems 1 -> True
276โˆ’ TupleItems fs | length fs == 1 -> True
261+ TupleItems fs | length fs == 1 -> True
277โˆ’ _ -> False
262+ _ -> False of
278โˆ’ of
279 True -> pure ()
263 True -> pure ()
280 False -> issueAt beh NoMatchingUniqueItems
264 False -> issueAt beh NoMatchingUniqueItems
281โˆ’
282 Properties props _ madd -> case foldSome (<>) prods $ \case
265 Properties props _ madd -> case foldSome (<>) prods $ \case
283โˆ’ Properties props' _ madd' -> Just $ (props', madd') NE.:| []
266+ Properties props' _ madd' -> Just $ (props', madd') NE.:| []
284โˆ’ _ -> Nothing
267+ _ -> Nothing of
285โˆ’ of
286 Just pm ->
268 Just pm ->
287 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
269 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
288 NE.toList pm <&> \(props', madd') -> do
270 NE.toList pm <&> \(props', madd') -> do
@@ -313,11 +295,9 @@ checkImplication env beh trs prods cons = case findExactly prods of
313 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
295 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
314 pure ()
296 pure ()
315 Nothing -> issueAt beh NoMatchingProperties
297 Nothing -> issueAt beh NoMatchingProperties
316โˆ’
317 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
298 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
318 MaxProperties m' -> Just m'
299 MaxProperties m' -> Just m'
319 _ -> Nothing
300 _ -> Nothing
320โˆ’
321 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
301 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
322 MinProperties m' -> Just m'
302 MinProperties m' -> Just m'
323 _ -> Nothing
303 _ -> Nothing
@@ -325,14 +305,14 @@ checkImplication env beh trs prods cons = case findExactly prods of
325 lcmScientific (toRational -> a) (toRational -> b) =
305 lcmScientific (toRational -> a) (toRational -> b) =
326 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
306 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
327
307
328โˆ’ foldCheck
308+ foldCheck ::
329โˆ’ :: Eq a
309+ Eq a =>
330โˆ’ => (a -> a -> a)
310+ (a -> a -> a) ->
331โˆ’ -> a
311+ a ->
332โˆ’ -> (a -> Issue 'TypedSchemaLevel)
312+ (a -> Issue 'TypedSchemaLevel) ->
333โˆ’ -> (ProdCons a -> Issue 'TypedSchemaLevel)
313+ (ProdCons a -> Issue 'TypedSchemaLevel) ->
334โˆ’ -> (forall t. Condition t -> Maybe a)
314+ (forall t. Condition t -> Maybe a) ->
335โˆ’ -> SemanticCompatFormula ()
315+ SemanticCompatFormula ()
336 foldCheck f m missing weak extr = case foldSome f prods extr of
316 foldCheck f m missing weak extr = case foldSome f prods extr of
337 Just m'
317 Just m'
338 | f m' m == m' -> pure ()
318 | f m' m == m' -> pure ()
src/OpenAPI/Checker/Validate/Schema/DNF.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/DNF.hsrenamed+9โˆ’9
@@ -1,12 +1,12 @@
1โˆ’module OpenAPI.Checker.Validate.Schema.DNF
1+module Data.OpenApi.Compare.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/OpenAPI/Checker/Validate/Schema/Issues.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/Issues.hsrenamed+95โˆ’24
@@ -1,21 +1,21 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.Schema.Issues
3+module Data.OpenApi.Compare.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
11import Data.Scientific
16import Data.Scientific
12import Data.Text (Text)
17import Data.Text (Text)
13import qualified Data.Text as T
18import 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,7 +87,67 @@ 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โˆ’ issueIsUnsupported _ = False
90+ issueKind = \case
91+ NoMatchingEnum _ -> ProbablyIssue
92+ MatchingMaximumWeak _ -> ProbablyIssue -- interplay with MultipleOf could make this not an issue
93+ MatchingMinimumWeak _ -> ProbablyIssue -- ditto
94+ MatchingMultipleOfWeak _ -> ProbablyIssue -- ditto
95+ NoMatchingFormat _ -> Unsupported
96+ NoMatchingPattern _ -> Unsupported
97+ ArrayToTuple -> Comment
98+ TupleToArray -> Comment
99+ NoMatchingProperties -> ProbablyIssue -- TODO: #109
100+ TypeBecomesEmpty -> ProbablyIssue -- TODO: #70
101+ PartitionBecomesEmpty _ -> ProbablyIssue -- ditto
102+ _ -> CertainIssue
103+ relatedIssues =
104+ (==) `withClass` \case
105+ EnumDoesntSatisfy v -> Just v
106+ NoMatchingEnum v -> Just v
107+ _ -> Nothing
108+ `withClass` \case
109+ NoMatchingMaximum _ -> Just ()
110+ MatchingMaximumWeak _ -> Just ()
111+ _ -> Nothing
112+ `withClass` \case
113+ NoMatchingMinimum _ -> Just ()
114+ MatchingMinimumWeak _ -> Just ()
115+ _ -> Nothing
116+ `withClass` \case
117+ NoMatchingMultipleOf _ -> Just ()
118+ MatchingMultipleOfWeak _ -> Just ()
119+ _ -> Nothing
120+ `withClass` \case
121+ NoMatchingMaxLength _ -> Just ()
122+ MatchingMaxLengthWeak _ -> Just ()
123+ _ -> Nothing
124+ `withClass` \case
125+ NoMatchingMinLength _ -> Just ()
126+ MatchingMinLengthWeak _ -> Just ()
127+ _ -> Nothing
128+ `withClass` \case
129+ NoMatchingItems -> Just ()
130+ ArrayToTuple -> Just ()
131+ TupleToArray -> Just ()
132+ NoMatchingTupleItems -> Just ()
133+ TupleItemsLengthChanged _ -> Just ()
134+ _ -> Nothing
135+ `withClass` \case
136+ NoMatchingMaxItems _ -> Just ()
137+ MatchingMaxItemsWeak _ -> Just ()
138+ _ -> Nothing
139+ `withClass` \case
140+ NoMatchingMinItems _ -> Just ()
141+ MatchingMinItemsWeak _ -> Just ()
142+ _ -> Nothing
143+ `withClass` \case
144+ NoMatchingMaxProperties _ -> Just ()
145+ MatchingMaxPropertiesWeak _ -> Just ()
146+ _ -> Nothing
147+ `withClass` \case
148+ NoMatchingMinProperties _ -> Just ()
149+ MatchingMinPropertiesWeak _ -> Just ()
150+ _ -> Nothing
91 describeIssue Forward (EnumDoesntSatisfy v) = para "The following enum value was removed:" <> showJSONValue v
151 describeIssue Forward (EnumDoesntSatisfy v) = para "The following enum value was removed:" <> showJSONValue v
92 describeIssue Backward (EnumDoesntSatisfy v) = para "The following enum value was added:" <> showJSONValue v
152 describeIssue Backward (EnumDoesntSatisfy v) = para "The following enum value was added:" <> showJSONValue v
93 describeIssue Forward (NoMatchingEnum v) = para "The following enum value has been added:" <> showJSONValue v
153 describeIssue Forward (NoMatchingEnum v) = para "The following enum value has been added:" <> showJSONValue v
@@ -186,15 +246,26 @@ instance Issuable 'SchemaLevel where
186 | -- | producer allows a property that is not allowed in the consumer
246 | -- | producer allows a property that is not allowed in the consumer
187 UnexpectedProperty
247 UnexpectedProperty
188 deriving stock (Eq, Ord, Show)
248 deriving stock (Eq, Ord, Show)
189โˆ’ issueIsUnsupported = \case
249+ issueKind = \case
190โˆ’ NotSupported _ -> True
250+ NotSupported _ -> Unsupported
191โˆ’ OneOfNotDisjoint -> True
251+ OneOfNotDisjoint -> Unsupported
192โˆ’ InvalidSchema _ -> True
252+ InvalidSchema _ -> SchemaInvalid
193โˆ’ UnguardedRecursion -> True
253+ UnguardedRecursion -> Unsupported
194โˆ’ _ -> False
254+ AdditionalToProperty -> Comment
195โˆ’
255+ PropertyToAdditional -> Comment
256+ TypesRestricted _ -> ProbablyIssue -- TODO: #70
257+ _ -> CertainIssue
258+ relatedIssues =
259+ (==) `withClass` \case
260+ AdditionalToProperty -> Just ()
261+ PropertyToAdditional -> Just ()
262+ _ -> Nothing
263+ `withClass` \case
264+ PropertyNowRequired -> Just ()
265+ UnexpectedProperty -> Just ()
266+ _ -> Nothing
196 describeIssue _ (NotSupported i) =
267 describeIssue _ (NotSupported i) =
197โˆ’ para (emph "Encountered a feature that OpenApi Diff does not support: " <> text i <> ".")
268+ para (emph "Encountered a feature that CompaREST does not support: " <> text i <> ".")
198 describeIssue _ OneOfNotDisjoint =
269 describeIssue _ OneOfNotDisjoint =
199 para $
270 para $
200 "Could not deduce that " <> code "oneOf"
271 "Could not deduce that " <> code "oneOf"
@@ -206,7 +277,7 @@ instance Issuable 'SchemaLevel where
206 describeIssue _ (InvalidSchema i) =
277 describeIssue _ (InvalidSchema i) =
207 para (emph "The schema is invalid: " <> text i <> ".")
278 para (emph "The schema is invalid: " <> text i <> ".")
208 describeIssue _ UnguardedRecursion =
279 describeIssue _ UnguardedRecursion =
209โˆ’ para "Encountered recursion that is too complex for OpenApi Diff to untangle."
280+ para "Encountered recursion that is too complex for CompaREST to untangle."
210 describeIssue Forward (TypesRestricted tys) = case tys of
281 describeIssue Forward (TypesRestricted tys) = case tys of
211 [] -> para "No longer has any valid values." -- weird
282 [] -> para "No longer has any valid values." -- weird
212 _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
283 _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
@@ -227,14 +298,14 @@ instance Behavable 'SchemaLevel 'TypedSchemaLevel where
227 = OfType JsonType
298 = OfType JsonType
228 deriving stock (Eq, Ord, Show)
299 deriving stock (Eq, Ord, Show)
229
300
230โˆ’ describeBehaviour (OfType t) = describeJSONType t
301+ describeBehavior (OfType t) = describeJSONType t
231
302
232instance Behavable 'TypedSchemaLevel 'TypedSchemaLevel where
303instance Behavable 'TypedSchemaLevel 'TypedSchemaLevel where
233 data Behave 'TypedSchemaLevel 'TypedSchemaLevel
304 data Behave 'TypedSchemaLevel 'TypedSchemaLevel
234 = InPartition Partition
305 = InPartition Partition
235 deriving stock (Eq, Ord, Show)
306 deriving stock (Eq, Ord, Show)
236
307
237โˆ’ describeBehaviour (InPartition partition) = "In cases where " <> showPartition partition
308+ describeBehavior (InPartition partition) = "In cases where " <> showPartition partition
238
309
239instance Behavable 'TypedSchemaLevel 'SchemaLevel where
310instance Behavable 'TypedSchemaLevel 'SchemaLevel where
240 data Behave 'TypedSchemaLevel 'SchemaLevel
311 data Behave 'TypedSchemaLevel 'SchemaLevel
@@ -244,7 +315,7 @@ instance Behavable 'TypedSchemaLevel 'SchemaLevel where
244 | InAdditionalProperty
315 | InAdditionalProperty
245 deriving stock (Eq, Ord, Show)
316 deriving stock (Eq, Ord, Show)
246
317
247โˆ’ describeBehaviour InItems = "Items"
318+ describeBehavior InItems = "Items"
248โˆ’ describeBehaviour (InItem i) = "Item " <> show' i
319+ describeBehavior (InItem i) = "Item " <> show' i
249โˆ’ describeBehaviour (InProperty p) = "Property " <> code p
320+ describeBehavior (InProperty p) = "Property " <> code p
250โˆ’ describeBehaviour InAdditionalProperty = "Additional properties"
321+ describeBehavior InAdditionalProperty = "Additional properties"
src/OpenAPI/Checker/Validate/Schema/JsonFormula.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/JsonFormula.hsrenamed+55โˆ’48
@@ -1,18 +1,18 @@
1โˆ’module OpenAPI.Checker.Validate.Schema.JsonFormula
1+module Data.OpenApi.Compare.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
@@ -20,13 +20,17 @@ import Algebra.Lattice
20import qualified Data.Aeson as A
20import qualified Data.Aeson as A
21import qualified Data.ByteString.Lazy as BSL
21import qualified Data.ByteString.Lazy as BSL
22import qualified Data.Foldable as F
22import qualified Data.Foldable as F
23โˆ’import qualified Data.HashMap.Strict as HM
24import Data.Functor
23import Data.Functor
24+import qualified Data.HashMap.Strict as HM
25import Data.Int
25import Data.Int
26import Data.Kind
26import Data.Kind
27import qualified Data.Map as M
27import qualified Data.Map as M
28import Data.Maybe
28import Data.Maybe
29import Data.OpenApi
29import Data.OpenApi
30+import Data.OpenApi.Compare.Orphans ()
31+import Data.OpenApi.Compare.Subtree
32+import Data.OpenApi.Compare.Validate.Schema.DNF
33+import Data.OpenApi.Compare.Validate.Schema.TypedJson
30import Data.Ord
34import Data.Ord
31import Data.Ratio
35import Data.Ratio
32import Data.Scientific
36import Data.Scientific
@@ -35,10 +39,6 @@ import Data.Text (Text)
35import qualified Data.Text as T
39import qualified Data.Text as T
36import qualified Data.Text.Encoding as T
40import qualified Data.Text.Encoding as T
37import Data.Typeable
41import 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
42import Text.Pandoc.Builder hiding (Format, Null)
42import Text.Pandoc.Builder hiding (Format, Null)
43import Text.Regex.Pcre2
43import Text.Regex.Pcre2
44
44
@@ -67,30 +67,33 @@ data Property = Property
67data Condition :: JsonType -> Type where
67data Condition :: JsonType -> Type where
68 Exactly :: TypedValue t -> Condition t
68 Exactly :: TypedValue t -> Condition t
69 Maximum :: !(Bound Scientific) -> Condition 'Number
69 Maximum :: !(Bound Scientific) -> Condition 'Number
70โˆ’ Minimum
70+ Minimum ::
71โˆ’ :: !(Down (Bound (Down Scientific)))
71+ !(Down (Bound (Down Scientific))) ->
72โˆ’ -> Condition 'Number -- ^ this has the right Ord
72+ -- | this has the right Ord
73+ Condition 'Number
73 MultipleOf :: !Scientific -> Condition 'Number
74 MultipleOf :: !Scientific -> Condition 'Number
74 NumberFormat :: !Format -> Condition 'Number
75 NumberFormat :: !Format -> Condition 'Number
75 MaxLength :: !Integer -> Condition 'String
76 MaxLength :: !Integer -> Condition 'String
76 MinLength :: !Integer -> Condition 'String
77 MinLength :: !Integer -> Condition 'String
77 Pattern :: !Pattern -> Condition 'String
78 Pattern :: !Pattern -> Condition 'String
78 StringFormat :: !Format -> Condition 'String
79 StringFormat :: !Format -> Condition 'String
79โˆ’ Items
80+ Items ::
80โˆ’ :: !(ForeachType JsonFormula)
81+ !(ForeachType JsonFormula) ->
81โˆ’ -> !(Traced (Referenced Schema))
82+ !(Traced (Referenced Schema)) ->
82โˆ’ -> Condition 'Array
83+ Condition 'Array
83โˆ’ TupleItems
84+ TupleItems ::
84โˆ’ :: ![(ForeachType JsonFormula, Traced (Referenced Schema))]
85+ ![(ForeachType JsonFormula, Traced (Referenced Schema))] ->
85โˆ’ -> Condition 'Array
86+ Condition 'Array
86 MaxItems :: !Integer -> Condition 'Array
87 MaxItems :: !Integer -> Condition 'Array
87 MinItems :: !Integer -> Condition 'Array
88 MinItems :: !Integer -> Condition 'Array
88 UniqueItems :: Condition 'Array
89 UniqueItems :: Condition 'Array
89โˆ’ Properties
90+ Properties ::
90โˆ’ :: !(M.Map Text Property)
91+ !(M.Map Text Property) ->
91โˆ’ -> !(ForeachType JsonFormula) -- ^ formula for additional properties
92+ -- | formula for additional properties
92โˆ’ -> !(Maybe (Traced (Referenced Schema))) -- ^ schema for additional properties, Nothing means bottom
93+ !(ForeachType JsonFormula) ->
93โˆ’ -> Condition 'Object
94+ -- | schema for additional properties, Nothing means bottom
95+ !(Maybe (Traced (Referenced Schema))) ->
96+ Condition 'Object
94 MaxProperties :: !Integer -> Condition 'Object
97 MaxProperties :: !Integer -> Condition 'Object
95 MinProperties :: !Integer -> Condition 'Object
98 MinProperties :: !Integer -> Condition 'Object
96
99
@@ -118,13 +121,15 @@ showCondition = \case
118 UniqueItems -> para "The elements in the array should be unique."
121 UniqueItems -> para "The elements in the array should be unique."
119 (Properties props additional _) ->
122 (Properties props additional _) ->
120 bulletList $
123 bulletList $
121โˆ’ (M.toList props
124+ ( M.toList props
122โˆ’ <&> (\(k, p) ->
125+ <&> ( \(k, p) ->
123โˆ’ para (code k)
126+ para (code k)
124โˆ’ <> para (strong $ if propRequired p then "Required" else "Optional")
127+ <> para (strong $ if propRequired p then "Required" else "Optional")
125โˆ’ <> showForEachJsonFormula (propFormula p)))
128+ <> showForEachJsonFormula (propFormula p)
129+ )
130+ )
126 <> [ para (emph "Additional properties")
131 <> [ para (emph "Additional properties")
127โˆ’ <> showForEachJsonFormula additional
132+ <> showForEachJsonFormula additional
128 ]
133 ]
129 (MaxProperties n) -> para $ "The maximum number of fields should be " <> show' n <> "."
134 (MaxProperties n) -> para $ "The maximum number of fields should be " <> show' n <> "."
130 (MinProperties n) -> para $ "The minimum number of fields should be " <> show' n <> "."
135 (MinProperties n) -> para $ "The minimum number of fields should be " <> show' n <> "."
@@ -133,15 +138,17 @@ showCondition = \case
133 showForEachJsonFormula i =
138 showForEachJsonFormula i =
134 bulletList $
139 bulletList $
135 foldType
140 foldType
136โˆ’ (\t f -> case getJsonFormula $ f i of
141+ ( \t f -> case getJsonFormula $ f i of
137โˆ’ BottomDNF -> mempty
142+ BottomDNF -> mempty
138โˆ’ (DNF conds) ->
143+ (DNF conds) ->
139 [ para (describeJSONType t)
144 [ para (describeJSONType t)
140 <> bulletList
145 <> bulletList
141โˆ’ (S.toList conds <&> \case
146+ ( S.toList conds <&> \case
142โˆ’ Disjunct (S.toList -> []) -> para "Empty"
147+ Disjunct (S.toList -> []) -> para "Empty"
143โˆ’ Disjunct (S.toList -> cond) -> bulletList (showCondition <$> cond))
148+ Disjunct (S.toList -> cond) -> bulletList (showCondition <$> cond)
144โˆ’ ])
149+ )
150+ ]
151+ )
145
152
146showJSONValue :: A.Value -> Blocks
153showJSONValue :: A.Value -> Blocks
147showJSONValue v = codeBlockWith ("", ["json"], mempty) (T.decodeUtf8 . BSL.toStrict . A.encode $ v)
154showJSONValue v = codeBlockWith ("", ["json"], mempty) (T.decodeUtf8 . BSL.toStrict . A.encode $ v)
src/OpenAPI/Checker/Validate/Schema/Partition.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/Partition.hsrenamed+38โˆ’37
@@ -1,15 +1,15 @@
1โˆ’module OpenAPI.Checker.Validate.Schema.Partition
1+module Data.OpenApi.Compare.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
@@ -32,17 +32,17 @@ import qualified Data.List.NonEmpty as NE
32import qualified Data.Map as M
32import qualified Data.Map as M
33import Data.Maybe
33import Data.Maybe
34import Data.OpenApi
34import Data.OpenApi
35+import Data.OpenApi.Compare.Memo
36+import Data.OpenApi.Compare.References
37+import Data.OpenApi.Compare.Subtree
38+import Data.OpenApi.Compare.Validate.Schema.DNF
39+import Data.OpenApi.Compare.Validate.Schema.JsonFormula
40+import Data.OpenApi.Compare.Validate.Schema.Traced
41+import Data.OpenApi.Compare.Validate.Schema.TypedJson
35import Data.Ord
42import Data.Ord
36import qualified Data.Set as S
43import qualified Data.Set as S
37import Data.Text (Text)
44import Data.Text (Text)
38import qualified Data.Text as T
45import qualified Data.Text as T
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
46import Text.Pandoc.Builder hiding (Format, Null)
46import Text.Pandoc.Builder hiding (Format, Null)
47
47
48data PartitionData
48data PartitionData
@@ -143,10 +143,11 @@ partitionCondition = \case
143 Just _ -> top
143 Just _ -> top
144 Nothing ->
144 Nothing ->
145 singletonPart $
145 singletonPart $
146โˆ’ DByProperties $ LiteralDNF
146+ DByProperties $
147โˆ’ ( M.keysSet $ M.filter (not . propRequired) props
147+ LiteralDNF
148โˆ’ , M.keysSet $ M.filter propRequired props
148+ ( M.keysSet $ M.filter (not . propRequired) props
149โˆ’ )
149+ , M.keysSet $ M.filter propRequired props
150+ )
150 inProps <- forM (M.toList $ M.filter propRequired props) $ \(k, prop) -> do
151 inProps <- forM (M.toList $ M.filter propRequired props) $ \(k, prop) -> do
151 f <- partitionRefSchema $ propRefSchema prop
152 f <- partitionRefSchema $ propRefSchema prop
152 pure $ fmap (\(Partitions m) -> Partitions $ M.mapKeysMonotonic (PInProperty k) m) f
153 pure $ fmap (\(Partitions m) -> Partitions $ M.mapKeysMonotonic (PInProperty k) m) f
@@ -156,10 +157,10 @@ partitionCondition = \case
156runPartitionM :: Traced (Definitions Schema) -> PartitionM a -> a
157runPartitionM :: Traced (Definitions Schema) -> PartitionM a -> a
157runPartitionM defs = runIdentity . runMemo () . (`runReaderT` defs)
158runPartitionM defs = runIdentity . runMemo () . (`runReaderT` defs)
158
159
159โˆ’partitionJsonFormulas
160+partitionJsonFormulas ::
160โˆ’ :: ProdCons (Traced (Definitions Schema))
161+ ProdCons (Traced (Definitions Schema)) ->
161โˆ’ -> ProdCons (JsonFormula t)
162+ ProdCons (JsonFormula t) ->
162โˆ’ -> Lifted Partitions
163+ Lifted Partitions
163partitionJsonFormulas defs pc = producer pcPart \/ consumer pcPart
164partitionJsonFormulas defs pc = producer pcPart \/ consumer pcPart
164 where
165 where
165 pcPart = partitionFormula <$> defs <*> pc
166 pcPart = partitionFormula <$> defs <*> pc
@@ -211,11 +212,11 @@ runIntersectionM defs act = case runWriterT $ runReaderT act defs of
211 Just (x, Any False) -> Same x
212 Just (x, Any False) -> Same x
212 Just (x, Any True) -> New x
213 Just (x, Any True) -> New x
213
214
214โˆ’intersectSchema
215+intersectSchema ::
215โˆ’ :: PartitionLocation
216+ PartitionLocation ->
216โˆ’ -> PartitionChoice
217+ PartitionChoice ->
217โˆ’ -> Traced Schema
218+ Traced Schema ->
218โˆ’ -> IntersectionM Schema
219+ IntersectionM Schema
219intersectSchema loc part sch = do
220intersectSchema loc part sch = do
220 allOf' <- forM (tracedAllOf sch) $ \rss ->
221 allOf' <- forM (tracedAllOf sch) $ \rss ->
221 -- Assuming i ranges over a nonempty set (checked in processSchema)
222 -- Assuming i ranges over a nonempty set (checked in processSchema)
@@ -258,11 +259,11 @@ intersectSchema loc part sch = do
258 pure $ sch' {_schemaEnum = Just enum'}
259 pure $ sch' {_schemaEnum = Just enum'}
259 CByProperties {} -> error "CByProperties not implemented"
260 CByProperties {} -> error "CByProperties not implemented"
260
261
261โˆ’intersectRefSchema
262+intersectRefSchema ::
262โˆ’ :: PartitionLocation
263+ PartitionLocation ->
263โˆ’ -> PartitionChoice
264+ PartitionChoice ->
264โˆ’ -> Traced (Referenced Schema)
265+ Traced (Referenced Schema) ->
265โˆ’ -> IntersectionM (Referenced Schema)
266+ IntersectionM (Referenced Schema)
266intersectRefSchema loc part rs = do
267intersectRefSchema loc part rs = do
267 defs <- R.ask
268 defs <- R.ask
268 Inline <$> intersectSchema loc part (dereference defs rs)
269 Inline <$> intersectSchema loc part (dereference defs rs)
src/OpenAPI/Checker/Validate/Schema/Process.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/Process.hsrenamed+48โˆ’47
@@ -1,11 +1,11 @@
1โˆ’module OpenAPI.Checker.Validate.Schema.Process
1+module Data.OpenApi.Compare.Validate.Schema.Process
2โˆ’ ( schemaToFormula
2+ ( schemaToFormula,
3 )
3 )
4where
4where
5
5
6import Algebra.Lattice
6import Algebra.Lattice
7โˆ’import qualified Control.Monad.Reader as R
8import Control.Monad.Reader hiding (ask)
7import Control.Monad.Reader hiding (ask)
8+import qualified Control.Monad.Reader as R
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
@@ -14,20 +14,20 @@ import 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
16import Data.OpenApi hiding (get)
16import Data.OpenApi hiding (get)
17+import Data.OpenApi.Compare.Behavior
18+import Data.OpenApi.Compare.Memo
19+import Data.OpenApi.Compare.Paths
20+import qualified Data.OpenApi.Compare.PathsPrefixTree as P
21+import Data.OpenApi.Compare.References
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.Traced
28+import Data.OpenApi.Compare.Validate.Schema.TypedJson
17import Data.Ord
29import Data.Ord
18import qualified Data.Set as S
30import qualified Data.Set as S
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
31
31
32-- | 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.
33newtype Silent w a = Silent {runSilent :: a}
33newtype Silent w a = Silent {runSilent :: a}
@@ -71,10 +71,10 @@ warnKnot =
71 , tieKnot = \_ -> pure
71 , tieKnot = \_ -> pure
72 }
72 }
73
73
74โˆ’processRefSchema
74+processRefSchema ::
75โˆ’ :: MonadProcess m
75+ MonadProcess m =>
76โˆ’ => Traced (Referenced Schema)
76+ Traced (Referenced Schema) ->
77โˆ’ -> m (ForeachType JsonFormula)
77+ m (ForeachType JsonFormula)
78processRefSchema x = do
78processRefSchema x = do
79 defs <- R.ask
79 defs <- R.ask
80 memoWithKnot warnKnot (processSchema $ dereference defs x) (ask x)
80 memoWithKnot warnKnot (processSchema $ dereference defs x) (ask x)
@@ -82,10 +82,10 @@ processRefSchema x = do
82-- | 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
83-- 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
84-- are thus checked independently.
84-- are thus checked independently.
85โˆ’processSchema
85+processSchema ::
86โˆ’ :: MonadProcess m
86+ MonadProcess m =>
87โˆ’ => Traced Schema
87+ Traced Schema ->
88โˆ’ -> m (ForeachType JsonFormula)
88+ m (ForeachType JsonFormula)
89processSchema sch@(extract -> Schema {..}) = do
89processSchema sch@(extract -> Schema {..}) = do
90 let singletonFormula :: Condition t -> JsonFormula t
90 let singletonFormula :: Condition t -> JsonFormula t
91 singletonFormula = JsonFormula . LiteralDNF
91 singletonFormula = JsonFormula . LiteralDNF
@@ -331,26 +331,27 @@ processSchema sch@(extract -> Schema {..}) = do
331 pure $
331 pure $
332 nullableClause
332 nullableClause
333 \/ meets
333 \/ meets
334โˆ’ (allClauses
334+ ( allClauses
335โˆ’ <> [ anyClause
335+ <> [ anyClause
336โˆ’ , oneClause
336+ , oneClause
337โˆ’ , typeClause
337+ , typeClause
338โˆ’ , enumClause
338+ , enumClause
339โˆ’ , maximumClause
339+ , maximumClause
340โˆ’ , minimumClause
340+ , minimumClause
341โˆ’ , multipleOfClause
341+ , multipleOfClause
342โˆ’ , formatClause
342+ , formatClause
343โˆ’ , maxLengthClause
343+ , maxLengthClause
344โˆ’ , minLengthClause
344+ , minLengthClause
345โˆ’ , patternClause
345+ , patternClause
346โˆ’ , itemsClause
346+ , itemsClause
347โˆ’ , maxItemsClause
347+ , maxItemsClause
348โˆ’ , minItemsClause
348+ , minItemsClause
349โˆ’ , uniqueItemsClause
349+ , uniqueItemsClause
350โˆ’ , propertiesClause
350+ , propertiesClause
351โˆ’ , maxPropertiesClause
351+ , maxPropertiesClause
352โˆ’ , minPropertiesClause
352+ , minPropertiesClause
353โˆ’ ])
353+ ]
354+ )
354
355
355{- TODO: ReadOnly/WriteOnly #68 -}
356{- TODO: ReadOnly/WriteOnly #68 -}
356
357
@@ -369,8 +370,8 @@ checkOneOfDisjoint schs = do
369runProcessM :: Traced (Definitions Schema) -> ProcessM a -> (a, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
370runProcessM :: Traced (Definitions Schema) -> ProcessM a -> (a, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
370runProcessM defs = runWriter . (`runReaderT` defs) . runMemo ()
371runProcessM defs = runWriter . (`runReaderT` defs) . runMemo ()
371
372
372โˆ’schemaToFormula
373+schemaToFormula ::
373โˆ’ :: Traced (Definitions Schema)
374+ Traced (Definitions Schema) ->
374โˆ’ -> Traced Schema
375+ Traced Schema ->
375โˆ’ -> (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
376+ (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
376schemaToFormula defs rs = runProcessM defs $ processSchema rs
377schemaToFormula defs rs = runProcessM defs $ processSchema rs
src/OpenAPI/Checker/Validate/Schema/Traced.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/Traced.hsrenamed+14โˆ’14
@@ -1,18 +1,18 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.Schema.Traced
3+module Data.OpenApi.Compare.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
24import qualified Data.Set as S
25import qualified Data.Set as S
25import Data.Text (Text)
26import 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/OpenAPI/Checker/Validate/Schema/TypedJson.hs โ†’ src/Data/OpenApi/Compare/Validate/Schema/TypedJson.hsrenamed+26โˆ’24
@@ -1,11 +1,11 @@
1โˆ’module OpenAPI.Checker.Validate.Schema.TypedJson
1+module Data.OpenApi.Compare.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,24 +88,26 @@ 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 = ForeachType
91+broadcastType k =
92โˆ’ { forNull = k
92+ ForeachType
93โˆ’ , forBoolean = k
93+ { forNull = k
94โˆ’ , forNumber = k
94+ , forBoolean = k
95โˆ’ , forString = k
95+ , forNumber = k
96โˆ’ , forArray = k
96+ , forString = k
97โˆ’ , forObject = k
97+ , forArray = k
98โˆ’ }
98+ , forObject = k
99+ }
99
100
100zipType :: (forall x. Typeable x => f x -> g x -> h x) -> ForeachType f -> ForeachType g -> ForeachType h
101zipType :: (forall x. Typeable x => f x -> g x -> h x) -> ForeachType f -> ForeachType g -> ForeachType h
101โˆ’zipType k f1 f2 = ForeachType
102+zipType k f1 f2 =
102โˆ’ { forNull = k (forNull f1) (forNull f2)
103+ ForeachType
103โˆ’ , forBoolean = k (forBoolean f1) (forBoolean f2)
104+ { forNull = k (forNull f1) (forNull f2)
104โˆ’ , forNumber = k (forNumber f1) (forNumber f2)
105+ , forBoolean = k (forBoolean f1) (forBoolean f2)
105โˆ’ , forString = k (forString f1) (forString f2)
106+ , forNumber = k (forNumber f1) (forNumber f2)
106โˆ’ , forArray = k (forArray f1) (forArray f2)
107+ , forString = k (forString f1) (forString f2)
107โˆ’ , forObject = k (forObject f1) (forObject f2)
108+ , forArray = k (forArray f1) (forArray f2)
108โˆ’ }
109+ , forObject = k (forObject f1) (forObject f2)
110+ }
109
111
110instance (forall x. Lattice (f x)) => Lattice (ForeachType f) where
112instance (forall x. Lattice (f x)) => Lattice (ForeachType f) where
111 (\/) = zipType (\/)
113 (\/) = zipType (\/)
src/OpenAPI/Checker/Validate/SecurityRequirement.hs โ†’ src/Data/OpenApi/Compare/Validate/SecurityRequirement.hsrenamed+14โˆ’13
@@ -1,8 +1,8 @@
1{-# LANGUAGE PartialTypeSignatures #-}
1{-# LANGUAGE PartialTypeSignatures #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
3
3
4โˆ’module OpenAPI.Checker.Validate.SecurityRequirement
4+module Data.OpenApi.Compare.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 ()
20import Data.Set (Set)
24import Data.Set (Set)
21import qualified Data.Set as S
25import qualified Data.Set as S
22import Data.Text (Text)
26import Data.Text (Text)
23import Data.Traversable
27import 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,18 +41,19 @@ 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+ )
49 normalized
50 normalized
50 pure ()
51 pure ()
51 checkSemanticCompatibility env bhv' pc = do
52 checkSemanticCompatibility env bhv' pc = do
52 let schemes = getH @(ProdCons (Traced (Definitions SecurityScheme))) env
53 let schemes = getH @(ProdCons (Traced (Definitions SecurityScheme))) env
53 ( ProdCons pErrs cErrs
54 ( ProdCons pErrs cErrs
54โˆ’ , (ProdCons pSchemes cSchemes)
55+ , (ProdCons pSchemes cSchemes) ::
55โˆ’ :: ProdCons [(Behavior 'SecuritySchemeLevel, Traced SecurityScheme, [Text])]
56+ ProdCons [(Behavior 'SecuritySchemeLevel, Traced SecurityScheme, [Text])]
56 ) =
57 ) =
57 NE.unzip $
58 NE.unzip $
58 partitionEithers <$> do
59 partitionEithers <$> do
src/OpenAPI/Checker/Validate/SecurityScheme.hs โ†’ src/Data/OpenApi/Compare/Validate/SecurityScheme.hsrenamed+5โˆ’5
@@ -1,16 +1,16 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.SecurityScheme
3+module Data.OpenApi.Compare.Validate.SecurityScheme
4 (
4 (
5 )
5 )
6where
6where
7
7
8import Control.Monad
8import Control.Monad
9import Data.OpenApi
9import Data.OpenApi
10โˆ’import OpenAPI.Checker.Behavior
10+import Data.OpenApi.Compare.Behavior
11โˆ’import OpenAPI.Checker.Orphans ()
11+import Data.OpenApi.Compare.Orphans ()
12โˆ’import OpenAPI.Checker.Subtree
12+import Data.OpenApi.Compare.Subtree
13โˆ’import OpenAPI.Checker.Validate.OAuth2Flows
13+import Data.OpenApi.Compare.Validate.OAuth2Flows
14
14
15instance Subtree SecurityScheme where
15instance Subtree SecurityScheme where
16 type CheckEnv SecurityScheme = '[]
16 type CheckEnv SecurityScheme = '[]
src/OpenAPI/Checker/Validate/Server.hs โ†’ src/Data/OpenApi/Compare/Validate/Server.hsrenamed+21โˆ’18
@@ -1,7 +1,7 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2
2
3โˆ’module OpenAPI.Checker.Validate.Server
3+module Data.OpenApi.Compare.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
22import qualified Data.Set as S
27import qualified Data.Set as S
23import Data.Text (Text)
28import Data.Text (Text)
24import qualified Data.Text as T
29import qualified Data.Text as T
25import Data.Traversable
30import 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โˆ’ describeBehaviour (InServer n) = "Server " <> code n
49+ describeBehavior (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,11 +63,12 @@ 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+ )
71 . sequence
72 . sequence
72 sequenceA_ pErrs
73 sequenceA_ pErrs
73 sequenceA_ cErrs
74 sequenceA_ cErrs
@@ -101,7 +102,7 @@ data ServerUrlPart var
101 deriving stock (Eq, Show, Functor, Foldable, Traversable)
102 deriving stock (Eq, Show, Functor, Foldable, Traversable)
102
103
103-- | This is super rough. Things like @{a|b}c@ will not match @ac@.
104-- | This is super rough. Things like @{a|b}c@ will not match @ac@.
104โˆ’-- FIXME: https://github.com/typeable/openapi-diff/issues/46
105+-- FIXME: #46
105--
106--
106-- NOTE: syntax is defined vaguely in the spec.
107-- NOTE: syntax is defined vaguely in the spec.
107parseServerUrl :: Text -> [ServerUrlPart Text]
108parseServerUrl :: Text -> [ServerUrlPart Text]
@@ -133,7 +134,9 @@ instance Issuable 'ServerLevel where
133 | ServerVariableNotDefined Text
134 | ServerVariableNotDefined Text
134 | ServerNotMatched
135 | ServerNotMatched
135 deriving stock (Eq, Ord, Show)
136 deriving stock (Eq, Ord, Show)
136โˆ’ issueIsUnsupported _ = False
137+ issueKind = \case
138+ ServerVariableNotDefined _ -> SchemaInvalid
139+ _ -> CertainIssue
137 describeIssue Forward (EnumValueNotConsumed _ v) =
140 describeIssue Forward (EnumValueNotConsumed _ v) =
138 para $ "Enum value " <> code v <> " has been removed."
141 para $ "Enum value " <> code v <> " has been removed."
139 describeIssue Backward (EnumValueNotConsumed _ v) =
142 describeIssue Backward (EnumValueNotConsumed _ v) =
src/Data/OpenApi/Compare/Validate/Sums.hsnew file+25
@@ -0,0 +1,25 @@
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/OpenAPI/Checker/Memo.hsdeletedโˆ’126
@@ -1,126 +0,0 @@
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/OpenAPI/Checker/Run.hsdeletedโˆ’30
@@ -1,30 +0,0 @@
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/OpenAPI/Checker/Validate/Products.hsdeletedโˆ’49
@@ -1,49 +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โˆ’
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/OpenAPI/Checker/Validate/Sums.hsdeletedโˆ’24
@@ -1,24 +0,0 @@
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+7โˆ’4
@@ -1,8 +1,11 @@
1โˆ’resolver: lts-18.2
1+resolver: lts-18.16
2+
3+packages:
4+ - .
2
5
3extra-deps:
6extra-deps:
4 - open-union-0.4.0.0
7 - open-union-0.4.0.0
5 - type-fun-0.1.3
8 - type-fun-0.1.3
6โˆ’
9+ - github-0.26
7โˆ’apply-ghc-options: everything
10+ - base16-bytestring-0.1.1.7
8โˆ’rebuild-ghc-options: true
11+ - http-link-header-1.0.3.1
stack.yaml.lock+25โˆ’4
@@ -18,9 +18,30 @@ 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
21snapshots:
42snapshots:
22- completed:
43- completed:
23โˆ’ size: 585392
44+ size: 586286
24โˆ’ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/2.yaml
45+ url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/16.yaml
25โˆ’ sha256: 7abb45c0cc5eb349448b66d8753655542d45d387ad26970419282eab3d860724
46+ sha256: cdead65fca0323144b346c94286186f4969bf85594d649c49c7557295675d8a5
26โˆ’ original: lts-18.2
47+ original: lts-18.16
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+46โˆ’42
@@ -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,32 +39,34 @@ 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โˆ’ -> FilePath -- ^ Root path
55+ -- | Root path
56โˆ’ -> FilePath -- ^ Name of golden file
56+ FilePath ->
57โˆ’ -> s
57+ -- | Name of golden file
58โˆ’ -> (t -> IO BSL.ByteString)
58+ FilePath ->
59โˆ’ -> IO TestTree
59+ s ->
60+ (t -> IO BSL.ByteString) ->
61+ IO TestTree
60goldenInputsTree name filepath golden inp f = do
62goldenInputsTree name filepath golden inp f = do
61 runTestInputTree golden f <$> getGoldenInputs name filepath inp
63 runTestInputTree golden f <$> getGoldenInputs name filepath inp
62
64
63โˆ’runTestInputTree
65+runTestInputTree ::
64โˆ’ :: FilePath
66+ FilePath ->
65โˆ’ -> (t -> IO BSL.ByteString)
67+ (t -> IO BSL.ByteString) ->
66โˆ’ -> TestInput t
68+ TestInput t ->
67โˆ’ -> TestTree
69+ TestTree
68runTestInputTree golden f (TestInputNode name rest) =
70runTestInputTree golden f (TestInputNode name rest) =
69 testGroup name (runTestInputTree golden f <$> rest)
71 testGroup name (runTestInputTree golden f <$> rest)
70runTestInputTree golden f (TestInputLeaf name t path) =
72runTestInputTree golden f (TestInputLeaf name t path) =
@@ -74,16 +76,18 @@ runTestInputTree golden f (TestInputLeaf name t path) =
74 (path </> golden)
76 (path </> golden)
75 (f t)
77 (f t)
76
78
77โˆ’goldenInputsTreeUniform
79+goldenInputsTreeUniform ::
78โˆ’ :: ( Each t h (FilePath, FilePath -> IO a) a
80+ ( Each t h (FilePath, FilePath -> IO a) a
79โˆ’ , Each s t FilePath (FilePath, FilePath -> IO a)
81+ , Each s t FilePath (FilePath, FilePath -> IO a)
80โˆ’ )
82+ ) =>
81โˆ’ => String
83+ String ->
82โˆ’ -> FilePath -- ^ Root path
84+ -- | Root path
83โˆ’ -> FilePath -- ^ Name of golden file
85+ FilePath ->
84โˆ’ -> s
86+ -- | Name of golden file
85โˆ’ -> (FilePath -> IO a)
87+ FilePath ->
86โˆ’ -> (h -> IO BSL.ByteString)
88+ s ->
87โˆ’ -> IO TestTree
89+ (FilePath -> IO a) ->
90+ (h -> IO BSL.ByteString) ->
91+ IO TestTree
88goldenInputsTreeUniform name filepath golden inp h =
92goldenInputsTreeUniform name filepath golden inp h =
89 goldenInputsTree name filepath golden (inp & each %~ (,h))
93 goldenInputsTree name filepath golden (inp & each %~ (,h))
test/Spec/Golden/TraceTree.hs+9โˆ’6
@@ -1,5 +1,5 @@
1module Spec.Golden.TraceTree
1module Spec.Golden.TraceTree
2โˆ’ ( tests
2+ ( tests,
3 )
3 )
4where
4where
5
5
@@ -7,13 +7,15 @@ 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 ()
10import Data.Text (Text)
12import Data.Text (Text)
11import qualified Data.Text.Encoding as T
13import qualified Data.Text.Encoding as T
12import qualified Data.Yaml as Yaml
14import qualified Data.Yaml as Yaml
13โˆ’import OpenAPI.Checker.Run
15+import Paths_compaREST
14โˆ’import OpenAPI.Checker.Validate.OpenApi ()
15import Spec.Golden.Extra
16import Spec.Golden.Extra
16import Test.Tasty (TestTree, testGroup)
17import Test.Tasty (TestTree, testGroup)
18+import Text.Pandoc.Builder
17import Text.Pandoc.Class
19import Text.Pandoc.Class
18import Text.Pandoc.Options
20import Text.Pandoc.Options
19import Text.Pandoc.Writers
21import Text.Pandoc.Writers
@@ -21,10 +23,11 @@ import Prelude hiding (id, (.))
21
23
22tests :: IO TestTree
24tests :: IO TestTree
23tests = do
25tests = do
26+ dir <- getDataDir
24 traceTreeTests <-
27 traceTreeTests <-
25 goldenInputsTreeUniform
28 goldenInputsTreeUniform
26 "TraceTree"
29 "TraceTree"
27โˆ’ "test/golden/common"
30+ dir
28 "trace-tree.yaml"
31 "trace-tree.yaml"
29 ("a.yaml", "b.yaml")
32 ("a.yaml", "b.yaml")
30 Yaml.decodeFileThrow
33 Yaml.decodeFileThrow
@@ -33,11 +36,11 @@ tests = do
33 reportTests <-
36 reportTests <-
34 goldenInputsTreeUniform
37 goldenInputsTreeUniform
35 "Report"
38 "Report"
36โˆ’ "test/golden/common"
39+ dir
37 "report.md"
40 "report.md"
38 ("a.yaml", "b.yaml")
41 ("a.yaml", "b.yaml")
39 Yaml.decodeFileThrow
42 Yaml.decodeFileThrow
40โˆ’ (runPandoc . writeMarkdown def {writerExtensions = githubMarkdownExtensions} . fst . runReport def)
43+ (runPandoc . writeMarkdown def {writerExtensions = githubMarkdownExtensions} . doc . fst . runReport def)
41
44
42 return $ testGroup "Golden tests" [traceTreeTests, reportTests]
45 return $ testGroup "Golden tests" [traceTreeTests, reportTests]
43
46
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -7,7 +7,7 @@ breakingChanges:
7 WithStatusCode 200:
7 WithStatusCode 200:
8 ResponsePayload:
8 ResponsePayload:
9 PayloadSchema: OneOfNotDisjoint
9 PayloadSchema: OneOfNotDisjoint
10โˆ’nonBreakingChanges:
10+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges:
8+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/enum-anyof/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
test/golden/common/id/report.md+3โˆ’3
@@ -1,5 +1,5 @@
1# Summary
1# Summary
2
2
3โˆ’| โš ๏ธ Breaking changes | ๐Ÿ™† Non-breaking changes | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/id/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/json/recursive/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -6,7 +6,7 @@ breakingChanges:
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โˆ’nonBreakingChanges:
9+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/trace-tree.yaml+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InParam "test1": ParamEmptinessIncompatible
4 InParam "test1": ParamEmptinessIncompatible
5โˆ’nonBreakingChanges: {}
5+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges:
7+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/pathItem/operation/parameters/required/false/add/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/pathItem/operation/parameters/required/false/del/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/parameters/required/set/trace-tree.yaml+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InParam "test1": ParamRequired
4 InParam "test1": ParamRequired
5โˆ’nonBreakingChanges: {}
5+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/parameters/required/true/add/trace-tree.yaml+2โˆ’2
@@ -1,4 +1,4 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod: ParamNotMatched "test2"
3 InOperation PostMethod: ParamNotMatched "test2"
4โˆ’nonBreakingChanges: {}
4+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges:
8+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/trace-tree.yaml+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges: {}
5+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/requestBody/required/set/trace-tree.yaml+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest: RequestBodyRequired
4 InRequest: RequestBodyRequired
5โˆ’nonBreakingChanges: {}
5+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/responses/add/trace-tree.yaml+2โˆ’2
@@ -1,4 +1,4 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
3 InOperation PostMethod: ConsumerDoesntHaveResponseCode 500
4โˆ’nonBreakingChanges: {}
4+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/trace-tree.yaml+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
4 WithStatusCode 200: ProducerDoesntHaveResponseHeader "Test2"
5โˆ’nonBreakingChanges: {}
5+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/pathItem/operation/responses/change/headers/optional/add/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/pathItem/operation/responses/change/headers/optional/del/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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 | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 1 | 0 | 0 |
5+| 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
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/trace-tree.yaml+2โˆ’2
@@ -1,5 +1,5 @@
1โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges: {}
5+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges:
8+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|---------------------|-------------------------------------------------|-------------------------------|
4+|---------------------|--------------------------------------------------|
5โˆ’| 0 | 1 | 0 |
5+| 0 | 1 |
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โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges:
2+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 3 | 3 | 0 |
5+| 3 | 3 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
@@ -9,7 +9,7 @@ breakingChanges:
9 - TypesRestricted [Number]
9 - TypesRestricted [Number]
10 - AdditionalToProperty
10 - AdditionalToProperty
11 - PropertyNowRequired
11 - PropertyNowRequired
12โˆ’nonBreakingChanges:
12+backwardChanges:
13 AtPath "/test":
13 AtPath "/test":
14 InOperation PostMethod:
14 InOperation PostMethod:
15 InRequest:
15 InRequest:
test/golden/common/property-removed/report.md+6โˆ’22
@@ -1,37 +1,21 @@
1# Summary
1# Summary
2
2
3โˆ’| [โš ๏ธ Breaking changes](#breaking-changes) | [๐Ÿ™† Non-breaking changes](#non-breaking-changes) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 2 | 2 | 0 |
5+| 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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -11,7 +11,7 @@ breakingChanges:
11 PayloadSchema:
11 PayloadSchema:
12 OfType Object:
12 OfType Object:
13 InProperty "property2": PropertyNowRequired
13 InProperty "property2": PropertyNowRequired
14โˆ’nonBreakingChanges:
14+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -6,7 +6,7 @@ breakingChanges:
6 PayloadSchema:
6 PayloadSchema:
7 OfType Object:
7 OfType Object:
8 InProperty "property2": PropertyNowRequired
8 InProperty "property2": PropertyNowRequired
9โˆ’nonBreakingChanges:
9+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 5 | 6 | 0 |
5+| 5 | 6 |
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+2โˆ’2
@@ -1,4 +1,4 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/pets":
2 AtPath "/pets":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
@@ -20,7 +20,7 @@ breakingChanges:
20 - TypesRestricted [Number]
20 - TypesRestricted [Number]
21 - AdditionalToProperty
21 - AdditionalToProperty
22 - OfType Number: NoMatchingMultipleOf 1.0
22 - OfType Number: NoMatchingMultipleOf 1.0
23โˆ’nonBreakingChanges:
23+backwardChanges:
24 AtPath "/pets":
24 AtPath "/pets":
25 InOperation GetMethod:
25 InOperation GetMethod:
26 WithStatusCode 200:
26 WithStatusCode 200:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/api/foo":
2 AtPath "/api/foo":
3 InOperation GetMethod:
3 InOperation GetMethod:
4 InRequest:
4 InRequest:
@@ -11,7 +11,7 @@ breakingChanges:
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โˆ’nonBreakingChanges:
14+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 2 | 1 | 0 |
5+| 2 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
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 @@ breakingChanges:
7 InOperation PostMethod:
7 InOperation PostMethod:
8 SecurityRequirementStep 0:
8 SecurityRequirementStep 0:
9 SecuritySchemeStep "oauth": SecuritySchemeNotMatched
9 SecuritySchemeStep "oauth": SecuritySchemeNotMatched
10โˆ’nonBreakingChanges:
10+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 3 | 1 | 0 |
5+| 3 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges:
8+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 2 | 2 | 0 |
5+| 2 | 2 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -13,7 +13,7 @@ breakingChanges:
13 OfType Array:
13 OfType Array:
14 InItem 1:
14 InItem 1:
15 OfType Object: TypeBecomesEmpty
15 OfType Object: TypeBecomesEmpty
16โˆ’nonBreakingChanges:
16+backwardChanges:
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+6โˆ’22
@@ -1,36 +1,20 @@
1# Summary
1# Summary
2
2
3โˆ’| [โš ๏ธ Breaking changes](#breaking-changes) | [๐Ÿ™† Non-breaking changes](#non-breaking-changes) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | โš ๏ธ Non-breaking changes |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|-------------------------|
5โˆ’| 2 | 2 | 0 |
5+| 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
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โˆ’
33โˆ’### ๐Ÿ“ฑโฌ…๏ธ JSON Response โ€“ 200
34
18
35#### `$(Array)`
19#### `$(Array)`
36
20
test/golden/common/tuple-items/heterogeneous-length-mismatch/trace-tree.yaml+2โˆ’2
@@ -1,4 +1,4 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -11,7 +11,7 @@ breakingChanges:
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โˆ’nonBreakingChanges:
14+backwardChanges:
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+9โˆ’17
@@ -1,14 +1,14 @@
1# Summary
1# Summary
2
2
3โˆ’| [โš ๏ธ Breaking changes](#breaking-changes) | [๐Ÿ™† Non-breaking changes](#non-breaking-changes) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 4 | 4 | 0 |
5+| 4 | 2 |
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,25 +28,17 @@ 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.
40
36
41#### `$[0](Number)`
37#### `$[0](Number)`
42
38
43The type has been added.
39The type has been added.
44
40
45โˆ’### ๐Ÿ“ฑโฌ…๏ธ JSON Response โ€“ 200
41+### โฌ…๏ธโ˜๏ธ JSON Response โ€“ 200
46โˆ’
47โˆ’#### `$(Array)`
48โˆ’
49โˆ’The array is no longer explicitly defined as a tuple.
50
42
51#### `$[0](String)`
43#### `$[0](String)`
52
44
test/golden/common/tuple-items/homogeneous-component-mismatch/trace-tree.yaml+2โˆ’2
@@ -1,4 +1,4 @@
1โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -15,7 +15,7 @@ breakingChanges:
15 - ArrayToTuple
15 - ArrayToTuple
16 - InItem 0:
16 - InItem 0:
17 OfType Number: TypeBecomesEmpty
17 OfType Number: TypeBecomesEmpty
18โˆ’nonBreakingChanges:
18+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 5 | 5 | 0 |
5+| 5 | 5 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test3":
2 AtPath "/test3":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -24,7 +24,7 @@ breakingChanges:
24 ResponsePayload:
24 ResponsePayload:
25 PayloadSchema:
25 PayloadSchema:
26 OfType Array: NoMatchingTupleItems
26 OfType Array: NoMatchingTupleItems
27โˆ’nonBreakingChanges:
27+backwardChanges:
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 | ๐Ÿคท Unsupported feature changes |
3+| โŒ Breaking changes | โš ๏ธ Non-breaking changes |
4โˆ’|---------------------|------------------------|-------------------------------|
4+|---------------------|-------------------------|
5โˆ’| 0 | 0 | 0 |
5+| 0 | 0 |
test/golden/common/tuple-items/homogeneous-vector/trace-tree.yaml+2โˆ’2
@@ -1,2 +1,2 @@
1โˆ’breakingChanges: {}
1+forwardChanges: {}
2โˆ’nonBreakingChanges: {}
2+backwardChanges: {}
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 6 | 6 | 0 |
5+| 6 | 6 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test5":
2 AtPath "/test5":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -28,7 +28,7 @@ breakingChanges:
28 InRequest:
28 InRequest:
29 InPayload:
29 InPayload:
30 PayloadSchema: TypesRestricted [Number,Array,Object]
30 PayloadSchema: TypesRestricted [Number,Array,Object]
31โˆ’nonBreakingChanges:
31+backwardChanges:
32 AtPath "/test5":
32 AtPath "/test5":
33 InOperation PostMethod:
33 InOperation PostMethod:
34 WithStatusCode 200:
34 WithStatusCode 200:
test/golden/common/unguarded-recursive/report.md+6โˆ’6
@@ -1,13 +1,13 @@
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 | 1 |
5+| 0 | 0 | 1 |
6
6
7โˆ’# <span id="unsupported-changes"></span>๐Ÿคท Unsupported feature changes
7+# <span id="unsupported-changes"></span>โ“ Unsupported feature changes
8
8
9## **GET** /api/foo
9## **GET** /api/foo
10
10
11โˆ’### ๐Ÿ“ฑโฌ…๏ธ JSON Response โ€“ 200
11+### โฌ…๏ธโ˜๏ธ JSON Response โ€“ 200
12
12
13โˆ’Encountered recursion that is too complex for OpenApi Diff to untangle.
13+Encountered recursion that is too complex for CompaREST to untangle.
test/golden/common/unguarded-recursive/trace-tree.yaml+2โˆ’2
@@ -1,10 +1,10 @@
1โˆ’breakingChanges:
1+forwardChanges:
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โˆ’nonBreakingChanges:
7+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 1 | 1 | 0 |
5+| 1 | 1 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 WithStatusCode 200:
4 WithStatusCode 200:
@@ -6,7 +6,7 @@ breakingChanges:
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โˆ’nonBreakingChanges:
9+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 2 | 2 | 0 |
5+| 2 | 2 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -15,7 +15,7 @@ breakingChanges:
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โˆ’nonBreakingChanges:
18+backwardChanges:
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) | ๐Ÿคท Unsupported feature changes |
3+| [โŒ Breaking changes](#breaking-changes) | [โš ๏ธ Non-breaking changes](#non-breaking-changes) |
4โˆ’|------------------------------------------|-------------------------------------------------|-------------------------------|
4+|------------------------------------------|--------------------------------------------------|
5โˆ’| 2 | 2 | 0 |
5+| 2 | 2 |
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โˆ’breakingChanges:
1+forwardChanges:
2 AtPath "/test":
2 AtPath "/test":
3 InOperation PostMethod:
3 InOperation PostMethod:
4 InRequest:
4 InRequest:
@@ -15,7 +15,7 @@ breakingChanges:
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โˆ’nonBreakingChanges:
18+backwardChanges:
19 AtPath "/test":
19 AtPath "/test":
20 InOperation PostMethod:
20 InOperation PostMethod:
21 InRequest:
21 InRequest: