diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ef615cc7..f7e564a85 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: working_directory: ~/openapi-diff docker: - - image: circleci/openjdk:8-jdk-node-browsers + - image: cimg/openjdk:8.0.442 steps: diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..92322c4e0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.idea/ +target/ diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 6d94e58c1..8c337389e 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -20,15 +20,15 @@ jobs: strategy: fail-fast: false matrix: - java_version: ['8', '11', '17'] + java_version: ['8', '11', '17', '21'] env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java_version }} @@ -38,8 +38,8 @@ jobs: server-username: OSS_USERNAME server-password: OSS_PASSWORD - name: Cache SonarCloud packages - uses: actions/cache@v2.1.7 - if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }} + uses: actions/cache@v4 + if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }} env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} with: @@ -49,7 +49,7 @@ jobs: - name: Run tests run: ./mvnw -V -B -ntp -ff verify jacoco:report - name: Static Analysis (Sonar) - if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }} + if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9b68efe15..d3a0a7bb7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -17,20 +17,20 @@ jobs: strategy: fail-fast: false matrix: - java_version: ['8', '11', '17'] + java_version: ['8', '11', '17', '21'] env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: ${{ matrix.java_version }} cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v2.1.7 - if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }} + uses: actions/cache@v4 + if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }} env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} with: @@ -40,7 +40,7 @@ jobs: - name: Run tests run: ./mvnw -V -B -ntp -ff verify - name: Static Analysis (Sonar) - if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }} + if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d5f5c4c6..94d5c8ba5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,9 +16,9 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 11 @@ -46,21 +46,24 @@ jobs: needs: release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.releaseVersion }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_PASSWORD }} - name: Build & Push Docker image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: context: . push: true + platforms: linux/amd64,linux/arm64 tags: | openapitools/openapi-diff:${{ github.event.inputs.releaseVersion }} openapitools/openapi-diff:latest diff --git a/.gitignore b/.gitignore index 7956bcbd3..7da72d7f2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ .classpath .project -.settings/* -.idea *.iml # Mobile Tools for Java (J2ME) @@ -17,3 +15,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* target/ + +# IDE files +.settings +.idea +.vscode diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..1dd7933ef --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,137 @@ +## Learn more about this file at 'https://www.gitpod.io/docs/references/gitpod-yml' +## +## This '.gitpod.yml' file when placed at the root of a project instructs +## Gitpod how to prepare & build the project, start development environments +## and configure continuous prebuilds. Prebuilds when enabled builds a project +## like a CI server so you can start coding right away - no more waiting for +## dependencies to download and builds to finish when reviewing pull-requests +## or hacking on something new. +## +## With Gitpod you can develop software from any device (even iPads) via +## desktop or browser based versions of VS Code or any JetBrains IDE and +## customise it to your individual needs - from themes to extensions, you +## have full control. +## +## The easiest way to try out Gitpod is install the browser extenion: +## 'https://www.gitpod.io/docs/browser-extension' or by prefixing +## 'https://gitpod.io#' to the source control URL of any project. +## +## For example: 'https://gitpod.io#https://github.com/gitpod-io/gitpod' + + +## The 'image' section defines which Docker image Gitpod should use. +## By default, Gitpod uses a standard Docker Image called 'workspace-full' +## which can be found at 'https://github.com/gitpod-io/workspace-images' +## +## Workspaces started based on this default image come pre-installed with +## Docker, Go, Java, Node.js, C/C++, Python, Ruby, Rust, PHP as well as +## tools such as Homebrew, Tailscale, Nginx and several more. +## +## If this image does not include the tools needed for your project then +## a public Docker image or your own Docker file can be configured. +## +## Learn more about images at 'https://www.gitpod.io/docs/config-docker' + +#image: node:buster # use 'https://hub.docker.com/_/node' +# +#image: # leave image undefined if using a Dockerfile +# file: .gitpod.Dockerfile # relative path to the Dockerfile from the +# # root of the project + +## The 'tasks' section defines how Gitpod prepares and builds this project +## or how Gitpod can start development servers. With Gitpod, there are three +## types of tasks: +## +## - before: Use this for tasks that need to run before init and before command. +## - init: Use this to configure prebuilds of heavy-lifting tasks such as +## downloading dependencies or compiling source code. +## - command: Use this to start your database or application when the workspace starts. +## +## Learn more about these tasks at 'https://www.gitpod.io/docs/config-start-tasks' + +#tasks: +# - before: | +# # commands to execute... +# +# - init: | +# # sudo apt-get install python3 # can be used to install operating system +# # dependencies but these are not kept after the +# # prebuild completes thus Gitpod recommends moving +# # operating system dependency installation steps +# # to a custom Dockerfile to make prebuilds faster +# # and to keep your codebase DRY. +# # 'https://www.gitpod.io/docs/config-docker' +# +# # pip install -r requirements.txt # install codebase dependencies +# # cmake # precompile codebase +# +# - name: Web Server +# openMode: split-left +# env: +# WEBSERVER_PORT: 8080 +# command: | +# python3 -m http.server $WEBSERVER_PORT +# +# - name: Web Browser +# openMode: split-right +# env: +# WEBSERVER_PORT: 8080 +# command: | +# gp await-port $WEBSERVER_PORT +# lynx `gp url` + +tasks: + - init: ./mvnw package -DskipTests + +## The 'ports' section defines various ports your may listen on are +## configured in Gitpod on an authenticated URL. By default, all ports +## are in private visibility state. +## +## Learn more about ports at 'https://www.gitpod.io/docs/config-ports' + +#ports: +# - port: 8080 # alternatively configure entire ranges via '8080-8090' +# visibility: private # either 'public' or 'private' (default) +# onOpen: open-browser # either 'open-browser', 'open-preview' or 'ignore' + + +## The 'vscode' section defines a list of Visual Studio Code extensions from +## the OpenVSX.org registry to be installed upon workspace startup. OpenVSX +## is an open alternative to the proprietary Visual Studio Code Marketplace +## and extensions can be added by sending a pull-request with the extension +## identifier to https://github.com/open-vsx/publish-extensions +## +## The identifier of an extension is always ${publisher}.${name}. +## +## For example: 'vscodevim.vim' +## +## Learn more at 'https://www.gitpod.io/docs/ides-and-editors/vscode' + +vscode: + extensions: + - redhat.java + - vscjava.vscode-java-pack + +## The 'github' section defines configuration of continuous prebuilds +## for GitHub repositories when the GitHub application +## 'https://github.com/apps/gitpod-io' is installed in GitHub and granted +## permissions to access the repository. +## +## Learn more at 'https://www.gitpod.io/docs/prebuilds' + +github: + prebuilds: + # enable for the default branch + master: true + # enable for all branches in this repo + branches: true + # enable for pull requests coming from this repo + pullRequests: true + # enable for pull requests coming from forks + pullRequestsFromForks: true + # add a check to pull requests + addCheck: true + # add a "Review in Gitpod" button as a comment to pull requests + addComment: true + # add a "Review in Gitpod" button to the pull request's description + addBadge: false diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar index c6feb8bb6..bf82ff01c 100644 Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 31158c7a0..687668b39 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,18 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip \ No newline at end of file +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/Dockerfile b/Dockerfile index 72bd50130..b07fd3dda 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,16 @@ -FROM openjdk:8-jdk-slim AS build +FROM eclipse-temurin:21-jdk-alpine AS build WORKDIR /build COPY ./ /build RUN ./mvnw -V -B -ff -P docker package -q -FROM openjdk:8-jre-slim +FROM eclipse-temurin:21-jre-alpine WORKDIR /app -COPY --from=build /build/cli/target/openapi-diff-cli-*-all.jar /app/openapi-diff.jar -ENTRYPOINT ["java", "-jar", "/app/openapi-diff.jar"] +COPY --from=build /build/cli/target/openapi-diff.jar /app/openapi-diff.jar +COPY --from=build /build/core/src/test/resources/petstore_v2_* /tmp +RUN java -XX:ArchiveClassesAtExit=/app/appcds.jsa \ + -jar /app/openapi-diff.jar \ + /tmp/petstore_v2_1.yaml \ + /tmp/petstore_v2_2.yaml \ + && rm -f /tmp/petstore_v2_* +ENTRYPOINT ["java", "-Xshare:on", "-XX:SharedArchiveFile=/app/appcds.jsa", "-jar", "/app/openapi-diff.jar"] CMD ["--help"] diff --git a/README.md b/README.md index 95648473e..e4979c1e3 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,17 @@ # OpenAPI-diff -Compare two OpenAPI specifications (3.x) and render the difference to HTML plaintext, or Markdown files. +Compare two OpenAPI specifications (3.x) and render the difference to HTML plaintext, Markdown files, or JSON files. [](https://github.com/OpenAPITools/openapi-diff/actions?query=branch%3Amaster+workflow%3A"Main+Build") [](https://sonarcloud.io/dashboard?id=OpenAPITools_openapi-diff) -[](https://search.maven.org/artifact/org.openapitools.openapidiff/openapi-diff-core) -[](https://join.slack.com/t/openapi-generator/shared_invite/enQtNzAyNDMyOTU0OTE1LTY5ZDBiNDI5NzI5ZjQ1Y2E5OWVjMjZkYzY1ZGM2MWQ4YWFjMzcyNDY5MGI4NjQxNDBiMTlmZTc5NjY2ZTQ5MGM) -[](https://hub.docker.com/r/openapitools/openapi-diff) -[](https://hub.docker.com/r/openapitools/openapi-diff) +[](https://search.maven.org/artifact/org.openapitools.openapidiff/openapi-diff-core) +[](https://search.maven.org/artifact/org.openapitools.openapidiff/openapi-diff-core) + +[](https://gitpod.io/#https://github.com/OpenAPITools/openapi-diff) +[](https://join.slack.com/t/openapi-generator/shared_invite/zt-12jxxd7p2-XUeQM~4pzsU9x~eGLQqX2g) + +[](https://hub.docker.com/r/openapitools/openapi-diff) [](https://hub.docker.com/r/openapitools/openapi-diff/tags) # Requirements @@ -21,7 +24,7 @@ Compare two OpenAPI specifications (3.x) and render the difference to HTML plain * Depth comparison of parameters, responses, endpoint, http method (GET,POST,PUT,DELETE...) * Supports swagger api Authorization * Render difference of property with Expression Language -* HTML, Markdown & JSON render +* HTML, Markdown, Asciidoc & JSON render # Maven @@ -35,6 +38,14 @@ Available on [Maven Central](https://search.maven.org/artifact/org.openapitools. </dependency> ``` +# Homebrew +Available for Mac users on [brew](https://formulae.brew.sh/formula/openapi-diff) + +```bash +brew install openapi-diff +``` +Usage instructions in [Usage -> Command line](#command-line) + # Docker Available on [Docker Hub](https://hub.docker.com/r/openapitools/openapi-diff/) as `openapitools/openapi-diff`. @@ -42,12 +53,15 @@ Available on [Docker Hub](https://hub.docker.com/r/openapitools/openapi-diff/) a ```bash # docker run openapitools/openapi-diff:latest usage: openapi-diff <old> <new> + --asciidoc <file> export diff as asciidoc in given file --debug Print debugging information --error Print error information --fail-on-changed Fail if API changed but is backward compatible --fail-on-incompatible Fail only if API changes broke backward compatibility + --config-file Config file to override default behavior. Supported file formats: .yaml + --config-prop Config property to override default behavior with key:value format (e.g. my.prop:true) -h,--help print this message --header <property=value> use given header for authorisation --html <file> export diff as html in given file @@ -66,7 +80,6 @@ usage: openapi-diff <old> <new> --warn Print warning information ``` - ## Build the image This is only required if you want to try new changes in the Dockerfile of this project. @@ -99,6 +112,7 @@ openapi-diff can read OpenAPI specs from JSON files or HTTP URLs. ```bash $ openapi-diff --help usage: openapi-diff <old> <new> + --asciidoc <file> export diff as asciidoc in given file --debug Print debugging information --error Print error information -h,--help print this message @@ -115,6 +129,8 @@ usage: openapi-diff <old> <new> incompatible, compatible --fail-on-incompatible Fail only if API changes broke backward compatibility --fail-on-changed Fail if API changed but is backward compatible + --config-file Config file to override default behavior. Supported file formats: .yaml + --config-prop Config property to override default behavior with key:value format (e.g. my.prop:true) --trace be extra verbose --version print the version information and exit --warn Print warning information @@ -142,7 +158,21 @@ Add openapi-diff to your POM to show diffs when you test your Maven project. You <!-- Fail only if API changes broke backward compatibility (default: false) --> <failOnIncompatible>true</failOnIncompatible> <!-- Fail if API changed (default: false) --> - <failOnChanged>true</failOnIncompatible> + <failOnChanged>true</failOnChanged> + <!-- Supply file path for console output to file if desired. --> + <consoleOutputFileName>${project.basedir}/../maven/target/diff.txt</consoleOutputFileName> + <!-- Supply file path for json output to file if desired. --> + <jsonOutputFileName>${project.basedir}/../maven/target/diff.json</jsonOutputFileName> + <!-- Supply file path for markdown output to file if desired. --> + <markdownOutputFileName>${project.basedir}/../maven/target/diff.md</markdownOutputFileName> + <!-- Supply config file(s), e.g. to disable incompatibility checks. Later files override earlier files --> + <configFiles> + <configFile>my/config-file.yaml</configFile> + </configFiles> + <!-- Supply config properties, e.g. to disable incompatibility checks. Overrides configFiles. --> + <configProps> + <incompatible.response.enum.increased>false</incompatible.response.enum.increased> + </configProps> </configuration> </execution> </executions> @@ -165,54 +195,42 @@ public class Main { ``` ### Render difference + --- #### HTML ```java -String html = new HtmlRender("Changelog", - "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(diff); - -try { - FileWriter fw = new FileWriter( - "testNewApi.html"); - fw.write(html); - fw.close(); - -} catch (IOException e) { - e.printStackTrace(); -} +HtmlRender htmlRender = new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); +FileOutputStream outputStream = new FileOutputStream("testDiff.html"); +OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); +htmlRender.render(diff, outputStreamWriter); ``` #### Markdown ```java -String render = new MarkdownRender().render(diff); -try { - FileWriter fw = new FileWriter( - "testDiff.md"); - fw.write(render); - fw.close(); - -} catch (IOException e) { - e.printStackTrace(); -} +MarkdownRender markdownRender = new MarkdownRender(); +FileOutputStream outputStream = new FileOutputStream("testDiff.md"); +OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); +markdownRender.render(diff, outputStreamWriter); ``` +#### Asciidoc + +```java +AsciidocRender asciidocRender = new AsciidocRender(); +FileOutputStream outputStream = new FileOutputStream("testDiff.adoc"); +OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); +asciidocRender.render(diff, outputStreamWriter); +``` #### JSON ```java -String render = new JsonRender().render(diff); -try { - FileWriter fw = new FileWriter( - "testDiff.json"); - fw.write(render); - fw.close(); - -} catch (IOException e) { - e.printStackTrace(); -} +JsonRender jsonRender = new JsonRender(); +FileOutputStream outputStream = new FileOutputStream("testDiff.json"); +OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); +jsonRender.render(diff, outputStreamWriter); ``` ### Extensions diff --git a/cli/pom.xml b/cli/pom.xml index 66c18fb22..573eb6c42 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.openapitools.openapidiff</groupId> <artifactId>openapi-diff-parent</artifactId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> </parent> <artifactId>openapi-diff-cli</artifactId> @@ -13,6 +13,10 @@ <name>openapi-diff-cli</name> <description>CLI for openapi-diff</description> + <properties> + <mainClass>org.openapitools.openapidiff.cli.Main</mainClass> + </properties> + <dependencies> <dependency> <groupId>org.openapitools.openapidiff</groupId> @@ -31,8 +35,8 @@ <artifactId>slf4j-api</artifactId> </dependency> <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-log4j12</artifactId> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> </dependency> </dependencies> <build> @@ -52,7 +56,7 @@ <shadedClassifierName>all</shadedClassifierName> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> - <mainClass>org.openapitools.openapidiff.cli.Main</mainClass> + <mainClass>${mainClass}</mainClass> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" /> @@ -64,4 +68,41 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>docker</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + <finalName>openapi-diff</finalName> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>${mainClass}</mainClass> + <manifestEntries> + <Implementation-Version>${project.version}</Implementation-Version> + </manifestEntries> + </transformer> + <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> + <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" /> + <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer" /> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java b/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java index 8da0fea16..a9e223d27 100644 --- a/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java +++ b/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java @@ -1,9 +1,11 @@ package org.openapitools.openapidiff.cli; +import ch.qos.logback.classic.Level; import io.swagger.v3.parser.core.models.AuthorizationValue; +import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; import java.util.Collections; import java.util.List; import org.apache.commons.cli.CommandLine; @@ -13,12 +15,11 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.output.AsciidocRender; import org.openapitools.openapidiff.core.output.ConsoleRender; import org.openapitools.openapidiff.core.output.HtmlRender; import org.openapitools.openapidiff.core.output.JsonRender; @@ -34,7 +35,10 @@ public static void main(String... args) { Options options = new Options(); options.addOption(Option.builder("h").longOpt("help").desc("print this message").build()); options.addOption( - Option.builder().longOpt("version").desc("print the version information and exit").build()); + Option.builder("v") + .longOpt("version") + .desc("print the version information and exit") + .build()); options.addOption( Option.builder() .longOpt("state") @@ -50,6 +54,19 @@ public static void main(String... args) { .longOpt("fail-on-changed") .desc("Fail if API changed but is backward compatible") .build()); + options.addOption( + Option.builder() + .longOpt("config-file") + .hasArg() + .desc("Config file to override default behavior. Supported file formats: .yaml") + .build()); + options.addOption( + Option.builder() + .longOpt("config-prop") + .hasArg() + .desc( + "Config property to override default behavior. Arg in format of [propKey]:[propVal]") + .build()); options.addOption(Option.builder().longOpt("trace").desc("be extra verbose").build()); options.addOption( Option.builder().longOpt("debug").desc("Print debugging information").build()); @@ -58,6 +75,7 @@ public static void main(String... args) { options.addOption(Option.builder().longOpt("warn").desc("Print warning information").build()); options.addOption(Option.builder().longOpt("error").desc("Print error information").build()); options.addOption(Option.builder().longOpt("off").desc("No information printed").build()); + options.addOption(Option.builder().longOpt("off").desc("No information printed").build()); options.addOption( Option.builder("l") .longOpt("log") @@ -90,12 +108,26 @@ public static void main(String... args) { .argName("file") .desc("export diff as markdown in given file") .build()); + options.addOption( + Option.builder() + .longOpt("asciidoc") + .hasArg() + .argName("file") + .desc("export diff as asciidoc in given file") + .build()); options.addOption( Option.builder() .longOpt("html") .hasArg() .argName("file") - .desc("export diff as html in given file") + .desc("export diff as html in given file with incompatible changes") + .build()); + options.addOption( + Option.builder() + .longOpt("html-detailed") + .hasArg() + .argName("file") + .desc("export diff as html in given file with all changes") .build()); options.addOption( Option.builder() @@ -121,6 +153,11 @@ public static void main(String... args) { printHelp(options); System.exit(0); } + if (line.hasOption("version") || line.hasOption("v")) { + String version = Main.class.getPackage().getImplementationVersion(); + System.out.println("openapi-diff version: " + (version != null ? version : "DEV")); + System.exit(0); + } String logLevel = "ERROR"; if (line.hasOption("off")) { logLevel = "OFF"; @@ -157,7 +194,9 @@ public static void main(String... args) { if (line.hasOption("state")) { logLevel = "OFF"; } - LogManager.getRootLogger().setLevel(Level.toLevel(logLevel)); + ch.qos.logback.classic.Logger root = + (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + root.setLevel(Level.toLevel(logLevel)); if (line.getArgList().size() < 2) { throw new ParseException("Missing arguments"); @@ -171,33 +210,68 @@ public static void main(String... args) { auths = Collections.singletonList(new AuthorizationValue(headers[0], headers[1], "header")); } - ChangedOpenApi result = OpenApiCompare.fromLocations(oldPath, newPath, auths); + OpenApiDiffOptions.Builder optionBuilder = OpenApiDiffOptions.builder(); + String[] configFilePaths = line.getOptionValues("config-file"); + if (configFilePaths != null) { + for (String configFilePath : configFilePaths) { + optionBuilder.configYaml(new File(configFilePath)); + } + } + + String[] configProps = line.getOptionValues("config-prop"); + if (configProps != null) { + for (String propKeyAndVal : configProps) { + String[] split = propKeyAndVal.split(":"); + if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) { + throw new IllegalArgumentException("--config-prop unexpected format: " + propKeyAndVal); + } + optionBuilder.configProperty(split[0], split[1]); + } + } + OpenApiDiffOptions compareOpts = optionBuilder.build(); + + ChangedOpenApi result = OpenApiCompare.fromLocations(oldPath, newPath, auths, compareOpts); ConsoleRender consoleRender = new ConsoleRender(); if (!logLevel.equals("OFF")) { - System.out.println(consoleRender.render(result)); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + consoleRender.render(result, outputStreamWriter); + System.out.println(outputStream); } if (line.hasOption("html")) { HtmlRender htmlRender = new HtmlRender(); - String output = htmlRender.render(result); - String outputFile = line.getOptionValue("html"); - writeOutput(output, outputFile); + FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("html")); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + htmlRender.render(result, outputStreamWriter); + } + if (line.hasOption("html-detailed")) { + HtmlRender htmlRender = new HtmlRender(true); + FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("html-detailed")); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + htmlRender.render(result, outputStreamWriter); } if (line.hasOption("markdown")) { MarkdownRender mdRender = new MarkdownRender(); - String output = mdRender.render(result); - String outputFile = line.getOptionValue("markdown"); - writeOutput(output, outputFile); + FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("markdown")); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + mdRender.render(result, outputStreamWriter); + } + if (line.hasOption("asciidoc")) { + AsciidocRender adocRender = new AsciidocRender(); + FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("asciidoc")); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + adocRender.render(result, outputStreamWriter); } if (line.hasOption("text")) { - String output = consoleRender.render(result); - String outputFile = line.getOptionValue("text"); - writeOutput(output, outputFile); + FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("text")); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + consoleRender.render(result, outputStreamWriter); } if (line.hasOption("json")) { JsonRender jsonRender = new JsonRender(); - String output = jsonRender.render(result); - String outputFile = line.getOptionValue("json"); - writeOutput(output, outputFile); + FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("json")); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + jsonRender.render(result, outputStreamWriter); } if (line.hasOption("state")) { System.out.println(result.isChanged().getValue()); @@ -222,17 +296,6 @@ public static void main(String... args) { } } - private static void writeOutput(String output, String outputFile) { - File file = new File(outputFile); - logger.debug("Output file: {}", file.getAbsolutePath()); - try { - FileUtils.writeStringToFile(file, output, StandardCharsets.UTF_8); - } catch (IOException e) { - logger.error("Impossible to write output to file {}", outputFile, e); - System.exit(2); - } - } - public static void printHelp(Options options) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("openapi-diff <old> <new>", options); diff --git a/cli/src/main/resources/log4j.properties b/cli/src/main/resources/log4j.properties deleted file mode 100644 index 1f7d27e30..000000000 --- a/cli/src/main/resources/log4j.properties +++ /dev/null @@ -1,5 +0,0 @@ -log4j.rootLogger=DEBUG, STDOUT -log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender -log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout -log4j.appender.STDOUT.layout.ConversionPattern=%-5p [%c] - %m%n -#log4j.appender.STDOUT.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c] - %m%n \ No newline at end of file diff --git a/cli/src/main/resources/logback.xml b/cli/src/main/resources/logback.xml new file mode 100644 index 000000000..018a4277c --- /dev/null +++ b/cli/src/main/resources/logback.xml @@ -0,0 +1,11 @@ +<configuration> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>[%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <root level="debug"> + <appender-ref ref="STDOUT"/> + </root> +</configuration> \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index 261e92e8c..0f056b319 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ <parent> <groupId>org.openapitools.openapidiff</groupId> <artifactId>openapi-diff-parent</artifactId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> </parent> <artifactId>openapi-diff-core</artifactId> @@ -25,6 +25,12 @@ <dependency> <groupId>io.swagger.parser.v3</groupId> <artifactId>swagger-parser-v2-converter</artifactId> + <exclusions> + <exclusion> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>com.j2html</groupId> @@ -34,13 +40,17 @@ <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-configuration2</artifactId> + </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> - <groupId>commons-httpclient</groupId> - <artifactId>commons-httpclient</artifactId> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> diff --git a/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java b/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java index 4564733a5..daa3af9b1 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java @@ -7,6 +7,7 @@ import java.io.File; import java.util.List; import org.openapitools.openapidiff.core.compare.OpenApiDiff; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; import org.openapitools.openapidiff.core.model.ChangedOpenApi; public class OpenApiCompare { @@ -40,7 +41,25 @@ public static ChangedOpenApi fromContents(String oldContent, String newContent) */ public static ChangedOpenApi fromContents( String oldContent, String newContent, List<AuthorizationValue> auths) { - return fromSpecifications(readContent(oldContent, auths), readContent(newContent, auths)); + return fromContents(oldContent, newContent, auths, OpenApiDiffOptions.builder().build()); + } + + /** + * compare two openapi doc + * + * @param oldContent old api-doc location:Json or Http + * @param newContent new api-doc location:Json or Http + * @param auths + * @param options + * @return Comparison result + */ + public static ChangedOpenApi fromContents( + String oldContent, + String newContent, + List<AuthorizationValue> auths, + OpenApiDiffOptions options) { + return fromSpecifications( + readContent(oldContent, auths), readContent(newContent, auths), options); } /** @@ -64,7 +83,21 @@ public static ChangedOpenApi fromFiles(File oldFile, File newFile) { */ public static ChangedOpenApi fromFiles( File oldFile, File newFile, List<AuthorizationValue> auths) { - return fromLocations(oldFile.getAbsolutePath(), newFile.getAbsolutePath(), auths); + return fromFiles(oldFile, newFile, auths, OpenApiDiffOptions.builder().build()); + } + + /** + * compare two openapi doc + * + * @param oldFile old api-doc file + * @param newFile new api-doc file + * @param auths + * @param options + * @return Comparison result + */ + public static ChangedOpenApi fromFiles( + File oldFile, File newFile, List<AuthorizationValue> auths, OpenApiDiffOptions options) { + return fromLocations(oldFile.getAbsolutePath(), newFile.getAbsolutePath(), auths, options); } /** @@ -88,7 +121,25 @@ public static ChangedOpenApi fromLocations(String oldLocation, String newLocatio */ public static ChangedOpenApi fromLocations( String oldLocation, String newLocation, List<AuthorizationValue> auths) { - return fromSpecifications(readLocation(oldLocation, auths), readLocation(newLocation, auths)); + return fromLocations(oldLocation, newLocation, auths, OpenApiDiffOptions.builder().build()); + } + + /** + * compare two openapi doc + * + * @param oldLocation old api-doc location (local or http) + * @param newLocation new api-doc location (local or http) + * @param auths + * @param options + * @return Comparison result + */ + public static ChangedOpenApi fromLocations( + String oldLocation, + String newLocation, + List<AuthorizationValue> auths, + OpenApiDiffOptions options) { + return fromSpecifications( + readLocation(oldLocation, auths), readLocation(newLocation, auths), options); } /** @@ -99,7 +150,20 @@ public static ChangedOpenApi fromLocations( * @return Comparison result */ public static ChangedOpenApi fromSpecifications(OpenAPI oldSpec, OpenAPI newSpec) { - return OpenApiDiff.compare(notNull(oldSpec, "old"), notNull(newSpec, "new")); + return fromSpecifications(oldSpec, newSpec, OpenApiDiffOptions.builder().build()); + } + + /** + * compare two openapi doc + * + * @param oldSpec old api-doc specification + * @param newSpec new api-doc specification + * @param options + * @return Comparison result + */ + public static ChangedOpenApi fromSpecifications( + OpenAPI oldSpec, OpenAPI newSpec, OpenApiDiffOptions options) { + return OpenApiDiff.compare(notNull(oldSpec, "old"), notNull(newSpec, "new"), options); } private static OpenAPI notNull(OpenAPI spec, String type) { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java index b647172db..7cf27dca7 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java @@ -5,7 +5,9 @@ import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; -import java.util.*; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import org.openapitools.openapidiff.core.model.Changed; import org.openapitools.openapidiff.core.model.ChangedContent; import org.openapitools.openapidiff.core.model.ChangedMediaType; @@ -34,22 +36,15 @@ public DeferredChanged<ChangedContent> diff(Content left, Content right, DiffCon MediaType oldMediaType = left.get(mediaTypeKey); MediaType newMediaType = right.get(mediaTypeKey); - ChangedMediaType changedMediaType = - new ChangedMediaType(oldMediaType.getSchema(), newMediaType.getSchema(), context); - builder .with( openApiDiff - .getSchemaDiff() - .diff( - oldMediaType.getSchema(), - newMediaType.getSchema(), - context.copyWithRequired(true))) + .getMediaTypeDiff() + .diff(oldMediaType, newMediaType, context.copyWithRequired(true))) .ifPresent( value -> { - changedMediaType.setSchema(value); - if (!isUnchanged(changedMediaType)) { - changedMediaTypes.put(mediaTypeKey, changedMediaType); + if (!isUnchanged(value)) { + changedMediaTypes.put(mediaTypeKey, value); } }); }); diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java index ecdfd5878..593c29df1 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java @@ -4,8 +4,9 @@ import io.swagger.v3.oas.models.headers.Header; import java.util.HashSet; import java.util.Objects; -import java.util.Optional; import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.ChangedExample; +import org.openapitools.openapidiff.core.model.ChangedExamples; import org.openapitools.openapidiff.core.model.ChangedHeader; import org.openapitools.openapidiff.core.model.DiffContext; import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder; @@ -44,12 +45,12 @@ protected DeferredChanged<ChangedHeader> computeDiff( DeferredBuilder<Changed> builder = new DeferredBuilder<>(); ChangedHeader changedHeader = new ChangedHeader(left, right, context) - .setRequired(getBooleanDiff(left.getRequired(), right.getRequired())) - .setDeprecated( - !Boolean.TRUE.equals(left.getDeprecated()) - && Boolean.TRUE.equals(right.getDeprecated())) + .setRequired(!Objects.equals(left.getRequired(), right.getRequired())) + .setDeprecated(!Objects.equals(left.getDeprecated(), right.getDeprecated())) .setStyle(!Objects.equals(left.getStyle(), right.getStyle())) - .setExplode(getBooleanDiff(left.getExplode(), right.getExplode())); + .setExplode(!Objects.equals(left.getExplode(), right.getExplode())) + .setExamples(new ChangedExamples(left.getExamples(), right.getExamples())) + .setExample(new ChangedExample(left.getExample(), right.getExample())); builder .with( openApiDiff @@ -73,10 +74,4 @@ protected DeferredChanged<ChangedHeader> computeDiff( .ifPresent(changedHeader::setExtensions); return builder.buildIsChanged(changedHeader); } - - private boolean getBooleanDiff(Boolean left, Boolean right) { - boolean leftRequired = Optional.ofNullable(left).orElse(Boolean.FALSE); - boolean rightRequired = Optional.ofNullable(right).orElse(Boolean.FALSE); - return leftRequired != rightRequired; - } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/MediaTypeDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/MediaTypeDiff.java new file mode 100644 index 000000000..a80324a92 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/MediaTypeDiff.java @@ -0,0 +1,40 @@ +package org.openapitools.openapidiff.core.compare; + +import static org.openapitools.openapidiff.core.utils.ChangedUtils.isChanged; + +import io.swagger.v3.oas.models.media.MediaType; +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.ChangedExample; +import org.openapitools.openapidiff.core.model.ChangedExamples; +import org.openapitools.openapidiff.core.model.ChangedMediaType; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder; +import org.openapitools.openapidiff.core.model.deferred.DeferredChanged; + +public class MediaTypeDiff { + + private final OpenApiDiff openApiDiff; + + public MediaTypeDiff(OpenApiDiff openApiDiff) { + this.openApiDiff = openApiDiff; + } + + public DeferredChanged<ChangedMediaType> diff( + MediaType left, MediaType right, DiffContext context) { + DeferredBuilder<Changed> builder = new DeferredBuilder<>(); + + ChangedMediaType changedMediaType = + new ChangedMediaType(left.getSchema(), right.getSchema(), context) + .setExample(new ChangedExample(left.getExample(), right.getExample())) + .setExamples(new ChangedExamples(left.getExamples(), right.getExamples())); + + builder + .with( + openApiDiff + .getSchemaDiff() + .diff(left.getSchema(), right.getSchema(), context.copyWithRequired(true))) + .ifPresent(changedMediaType::setSchema); + + return builder.build().mapOptional(value -> isChanged(changedMediaType)); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java index 48e5e0447..8dd404f5b 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java @@ -8,6 +8,7 @@ import java.util.Objects; import java.util.Optional; import org.openapitools.openapidiff.core.model.ChangedOAuthFlow; +import org.openapitools.openapidiff.core.model.DiffContext; public class OAuthFlowDiff { private final OpenApiDiff openApiDiff; @@ -20,8 +21,8 @@ private static Map<String, Object> getExtensions(OAuthFlow oAuthFlow) { return ofNullable(oAuthFlow).map(OAuthFlow::getExtensions).orElse(null); } - public Optional<ChangedOAuthFlow> diff(OAuthFlow left, OAuthFlow right) { - ChangedOAuthFlow changedOAuthFlow = new ChangedOAuthFlow(left, right); + public Optional<ChangedOAuthFlow> diff(OAuthFlow left, OAuthFlow right, DiffContext context) { + ChangedOAuthFlow changedOAuthFlow = new ChangedOAuthFlow(left, right, context); if (left != null && right != null) { changedOAuthFlow .setAuthorizationUrl( diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java index 98ceb509f..2b1136618 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Optional; import org.openapitools.openapidiff.core.model.ChangedOAuthFlows; +import org.openapitools.openapidiff.core.model.DiffContext; public class OAuthFlowsDiff { private final OpenApiDiff openApiDiff; @@ -19,24 +20,24 @@ private static Map<String, Object> getExtensions(OAuthFlows oAuthFlow) { return ofNullable(oAuthFlow).map(OAuthFlows::getExtensions).orElse(null); } - public Optional<ChangedOAuthFlows> diff(OAuthFlows left, OAuthFlows right) { + public Optional<ChangedOAuthFlows> diff(OAuthFlows left, OAuthFlows right, DiffContext context) { ChangedOAuthFlows changedOAuthFlows = new ChangedOAuthFlows(left, right); if (left != null && right != null) { openApiDiff .getOAuthFlowDiff() - .diff(left.getImplicit(), right.getImplicit()) + .diff(left.getImplicit(), right.getImplicit(), context) .ifPresent(changedOAuthFlows::setImplicitOAuthFlow); openApiDiff .getOAuthFlowDiff() - .diff(left.getPassword(), right.getPassword()) + .diff(left.getPassword(), right.getPassword(), context) .ifPresent(changedOAuthFlows::setPasswordOAuthFlow); openApiDiff .getOAuthFlowDiff() - .diff(left.getClientCredentials(), right.getClientCredentials()) + .diff(left.getClientCredentials(), right.getClientCredentials(), context) .ifPresent(changedOAuthFlows::setClientCredentialOAuthFlow); openApiDiff .getOAuthFlowDiff() - .diff(left.getAuthorizationCode(), right.getAuthorizationCode()) + .diff(left.getAuthorizationCode(), right.getAuthorizationCode(), context) .ifPresent(changedOAuthFlows::setAuthorizationCodeOAuthFlow); } openApiDiff diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java index 1f0fe03bf..c1bb414d8 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java @@ -24,6 +24,7 @@ public class OpenApiDiff { private PathDiff pathDiff; private SchemaDiff schemaDiff; private ContentDiff contentDiff; + private MediaTypeDiff mediaTypeDiff; private ParametersDiff parametersDiff; private ParameterDiff parameterDiff; private RequestBodyDiff requestBodyDiff; @@ -41,6 +42,7 @@ public class OpenApiDiff { private MetadataDiff metadataDiff; private final OpenAPI oldSpecOpenApi; private final OpenAPI newSpecOpenApi; + private final OpenApiDiffOptions options; private List<Endpoint> newEndpoints; private List<Endpoint> missingEndpoints; private List<ChangedOperation> changedOperations; @@ -50,18 +52,24 @@ public class OpenApiDiff { /* * @param oldSpecOpenApi * @param newSpecOpenApi + * @param diffOptions */ - private OpenApiDiff(OpenAPI oldSpecOpenApi, OpenAPI newSpecOpenApi) { + private OpenApiDiff(OpenAPI oldSpecOpenApi, OpenAPI newSpecOpenApi, OpenApiDiffOptions options) { this.oldSpecOpenApi = oldSpecOpenApi; this.newSpecOpenApi = newSpecOpenApi; + this.options = options; if (null == oldSpecOpenApi || null == newSpecOpenApi) { throw new RuntimeException("one of the old or new object is null"); } + if (null == options) { + throw new IllegalArgumentException("options parameter is null but is required"); + } initializeFields(); } - public static ChangedOpenApi compare(OpenAPI oldSpec, OpenAPI newSpec) { - return new OpenApiDiff(oldSpec, newSpec).compare(); + public static ChangedOpenApi compare( + OpenAPI oldSpec, OpenAPI newSpec, OpenApiDiffOptions diffOptions) { + return new OpenApiDiff(oldSpec, newSpec, diffOptions).compare(); } private void initializeFields() { @@ -69,6 +77,7 @@ private void initializeFields() { this.pathDiff = new PathDiff(this); this.schemaDiff = new SchemaDiff(this); this.contentDiff = new ContentDiff(this); + this.mediaTypeDiff = new MediaTypeDiff(this); this.parametersDiff = new ParametersDiff(this); this.parameterDiff = new ParameterDiff(this); this.requestBodyDiff = new RequestBodyDiff(this); @@ -87,6 +96,10 @@ private void initializeFields() { this.deferredSchemaCache = new DeferredSchemaCache(this); } + public OpenApiDiffOptions getOptions() { + return options; + } + private ChangedOpenApi compare() { preProcess(oldSpecOpenApi); preProcess(newSpecOpenApi); @@ -163,7 +176,7 @@ private void preProcess(OpenAPI openApi) { } private ChangedOpenApi getChangedOpenApi() { - return new ChangedOpenApi() + return new ChangedOpenApi(options) .setMissingEndpoints(missingEndpoints) .setNewEndpoints(newEndpoints) .setNewSpecOpenApi(newSpecOpenApi) @@ -193,6 +206,10 @@ public ContentDiff getContentDiff() { return this.contentDiff; } + public MediaTypeDiff getMediaTypeDiff() { + return this.mediaTypeDiff; + } + public ParametersDiff getParametersDiff() { return this.parametersDiff; } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java new file mode 100644 index 000000000..23f4e1c37 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java @@ -0,0 +1,49 @@ +package org.openapitools.openapidiff.core.compare; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import org.apache.commons.configuration2.CompositeConfiguration; +import org.apache.commons.configuration2.YAMLConfiguration; +import org.apache.commons.configuration2.ex.ConfigurationException; + +public class OpenApiDiffOptions { + private final CompositeConfiguration config; + + private OpenApiDiffOptions(CompositeConfiguration config) { + this.config = config; + } + + public CompositeConfiguration getConfig() { + return config; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private OpenApiDiffOptions built = new OpenApiDiffOptions(new CompositeConfiguration()); + + public Builder configYaml(File file) { + YAMLConfiguration yamlConfig = new YAMLConfiguration(); + try { + yamlConfig.read(new FileReader(file)); + } catch (ConfigurationException | FileNotFoundException e) { + throw new IllegalArgumentException("Problem loading config. file=" + file, e); + } + // Ideally immutable, but since it isn't, we just modify the config directly + built.getConfig().addConfigurationFirst(yamlConfig); + return this; + } + + public Builder configProperty(String propKey, String propVal) { + built.getConfig().setProperty(propKey, propVal); + return this; + } + + public OpenApiDiffOptions build() { + return built; + } + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java index a2bc17244..b44093263 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java @@ -57,8 +57,7 @@ public DeferredChanged<ChangedOperation> diff( .diff(oldOperation.getOperationId(), newOperation.getOperationId(), context)) .ifPresent(changedOperation::setOperationId); changedOperation.setDeprecated( - !Boolean.TRUE.equals(oldOperation.getDeprecated()) - && Boolean.TRUE.equals(newOperation.getDeprecated())); + !Objects.equals(oldOperation.getDeprecated(), newOperation.getDeprecated())); if (oldOperation.getRequestBody() != null || newOperation.getRequestBody() != null) { builder @@ -72,14 +71,17 @@ public DeferredChanged<ChangedOperation> diff( .ifPresent(changedOperation::setRequestBody); } + ParametersDiffResult parametersDiffResult = + openApiDiff + .getParametersDiff() + .diff(oldOperation.getParameters(), newOperation.getParameters(), context); builder - .with( - openApiDiff - .getParametersDiff() - .diff(oldOperation.getParameters(), newOperation.getParameters(), context)) + .with(parametersDiffResult.deferredChanged) .ifPresent( params -> { - removePathParameters(context.getParameters(), params); + if (!parametersDiffResult.sameOperationsDiffSchema) { + removePathParameters(context.getParameters(), params); + } changedOperation.setParameters(params); }); diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java index 6652b0bc4..3a8389d16 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java @@ -4,8 +4,9 @@ import io.swagger.v3.oas.models.parameters.Parameter; import java.util.HashSet; import java.util.Objects; -import java.util.Optional; import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.ChangedExample; +import org.openapitools.openapidiff.core.model.ChangedExamples; import org.openapitools.openapidiff.core.model.ChangedParameter; import org.openapitools.openapidiff.core.model.DiffContext; import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder; @@ -49,14 +50,14 @@ protected DeferredChanged<ChangedParameter> computeDiff( new ChangedParameter(right.getName(), right.getIn(), context) .setOldParameter(left) .setNewParameter(right) - .setChangeRequired(getBooleanDiff(left.getRequired(), right.getRequired())) - .setDeprecated( - !Boolean.TRUE.equals(left.getDeprecated()) - && Boolean.TRUE.equals(right.getDeprecated())) + .setChangeRequired(!Objects.equals(left.getRequired(), right.getRequired())) + .setDeprecated(!Objects.equals(left.getDeprecated(), right.getDeprecated())) .setChangeAllowEmptyValue( - getBooleanDiff(left.getAllowEmptyValue(), right.getAllowEmptyValue())) + !Objects.equals(left.getAllowEmptyValue(), right.getAllowEmptyValue())) .setChangeStyle(!Objects.equals(left.getStyle(), right.getStyle())) - .setChangeExplode(getBooleanDiff(left.getExplode(), right.getExplode())); + .setChangeExplode(!Objects.equals(left.getExplode(), right.getExplode())) + .setExamples(new ChangedExamples(left.getExamples(), right.getExamples())) + .setExample(new ChangedExample(left.getExample(), right.getExample())); builder .with( openApiDiff @@ -80,10 +81,4 @@ protected DeferredChanged<ChangedParameter> computeDiff( .ifPresent(changedParameter::setExtensions); return builder.buildIsChanged(changedParameter); } - - private boolean getBooleanDiff(Boolean left, Boolean right) { - boolean leftRequired = Optional.ofNullable(left).orElse(Boolean.FALSE); - boolean rightRequired = Optional.ofNullable(right).orElse(Boolean.FALSE); - return leftRequired != rightRequired; - } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java index c7fb99b08..6de3b4c28 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java @@ -3,9 +3,13 @@ import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.parameters.Parameter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; import org.openapitools.openapidiff.core.model.Changed; import org.openapitools.openapidiff.core.model.ChangedParameters; import org.openapitools.openapidiff.core.model.DiffContext; @@ -14,8 +18,19 @@ import org.openapitools.openapidiff.core.utils.RefPointer; import org.openapitools.openapidiff.core.utils.RefType; +class ParametersDiffResult { + protected DeferredChanged<ChangedParameters> deferredChanged; + protected boolean sameOperationsDiffSchema; + + public ParametersDiffResult( + DeferredChanged<ChangedParameters> deferredChanged, boolean sameOperationsDiffSchema) { + this.deferredChanged = deferredChanged; + this.sameOperationsDiffSchema = sameOperationsDiffSchema; + } +} /** compare two parameter */ public class ParametersDiff { + private static final RefPointer<Parameter> refPointer = new RefPointer<>(RefType.PARAMETERS); private final Components leftComponents; @@ -46,31 +61,86 @@ public static boolean same(Parameter left, Parameter right) { && Objects.equals(left.getIn(), right.getIn()); } - public DeferredChanged<ChangedParameters> diff( - List<Parameter> left, List<Parameter> right, DiffContext context) { + public ParametersDiffResult diff( + final List<Parameter> left, final List<Parameter> right, final DiffContext context) { + final DeferredBuilder<Changed> builder = new DeferredBuilder<>(); + final List<Parameter> wLeft = Optional.ofNullable(left).orElseGet(Collections::emptyList); + final List<Parameter> wRight = + Optional.ofNullable(right).map(ArrayList::new).orElseGet(ArrayList::new); - DeferredBuilder<Changed> builder = new DeferredBuilder<>(); - ChangedParameters changedParameters = - new ChangedParameters(left, right != null ? new ArrayList<>(right) : null, context); - if (null == left) left = new ArrayList<>(); - if (null == right) right = new ArrayList<>(); + final ChangedParameters changedParameters = new ChangedParameters(wLeft, wRight, context); - for (Parameter leftPara : left) { - leftPara = refPointer.resolveRef(leftComponents, leftPara, leftPara.get$ref()); - - Optional<Parameter> rightParam = contains(rightComponents, right, leftPara); - if (!rightParam.isPresent()) { - changedParameters.getMissing().add(leftPara); + for (Parameter leftParam : wLeft) { + leftParam = refPointer.resolveRef(leftComponents, leftParam, leftParam.get$ref()); + Optional<Parameter> rightParamOpt = contains(rightComponents, wRight, leftParam); + if (!rightParamOpt.isPresent()) { + changedParameters.getMissing().add(leftParam); } else { - Parameter rightPara = rightParam.get(); - right.remove(rightPara); + Parameter rightParam = rightParamOpt.get(); + wRight.remove(rightParam); builder - .with(openApiDiff.getParameterDiff().diff(leftPara, rightPara, context)) + .with(openApiDiff.getParameterDiff().diff(leftParam, rightParam, context)) .ifPresent(changedParameters.getChanged()::add); } } - changedParameters.getIncreased().addAll(right); + changedParameters.getIncreased().addAll(wRight); + return new ParametersDiffResult( + builder.buildIsChanged(changedParameters), + pathUnchangedParametersChanged(changedParameters, context)); + } + + public boolean pathUnchangedParametersChanged( + ChangedParameters changedParameters, DiffContext context) { + if (!pathUnchanged(changedParameters, context)) return false; + // If missing and increased parameter count is different, it's a new endpoint + if (changedParameters.getMissing().size() != changedParameters.getIncreased().size()) + return false; + // Go through each missing Parameter and compare it to newly added Parameters + for (Parameter parameter : changedParameters.getMissing()) { + // Speedy Check. Use the map already created in changedParameters to check if missing param is + // linked to newParam + String newParameterName = context.getParameters().get(parameter.getName()); + if (StringUtils.isBlank(newParameterName)) return false; + + Optional<Parameter> newParameter = + changedParameters.getIncreased().stream() + .filter(p -> p.getName().equals(newParameterName)) + .findFirst(); + if (!newParameter.isPresent()) return false; - return builder.buildIsChanged(changedParameters); + // Check if the old and new Parameters match . IF so, return TRUE + Parameter newParameterRealized = newParameter.get(); + newParameterRealized.setName(parameter.getName()); // Make names similar + boolean samePathDifferentParameter = !newParameterRealized.equals(parameter); + newParameterRealized.setName( + newParameterName); // Important:: MUST Reset the name as this is not a copy + if (samePathDifferentParameter) { + return true; + } + } + return false; + } + + public boolean pathUnchanged(ChangedParameters changedParameters, DiffContext context) { + final String REGEX_PATH = "\\{([^/]+)}"; + String oldUrl = context.getLeftUrl(); + String newUrl = context.getRightUrl(); + ArrayList<String> oldUrlPathParams = matchedItems(oldUrl, REGEX_PATH); + ArrayList<String> newUrlPathParams = matchedItems(newUrl, REGEX_PATH); + // Path Param count doesn't match or param-less path doesn't match or param is changed --> It's + // a new endpoint + return oldUrlPathParams.size() == newUrlPathParams.size() + && changedParameters.getChanged().isEmpty() + && oldUrl.replaceAll(REGEX_PATH, "").equals(newUrl.replaceAll(REGEX_PATH, "")); + } + + public ArrayList<String> matchedItems(String string, String regex) { + Matcher matcher = Pattern.compile(regex).matcher(string); + ArrayList<String> matchedItems = new ArrayList<>(); + while (matcher.find()) { + String item = matcher.group(); + matchedItems.add(item.substring(0, matcher.group().length() - 1).replaceFirst("\\{", "")); + } + return matchedItems; } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java index 0537907a0..f695ef083 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java @@ -36,7 +36,12 @@ public DeferredChanged<ChangedPath> diff(PathItem left, PathItem right, DiffCont .with( openApiDiff .getOperationDiff() - .diff(oldOperation, newOperation, context.copyWithMethod(method))) + .diff( + oldOperation, + newOperation, + context + .copyWithMethod(method) + .copyWithLeftRightUrls(context.getLeftUrl(), context.getRightUrl()))) .ifPresent(changedPath.getChanged()::add); } builder diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java index 0b978cdff..8b4f459b8 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java @@ -1,10 +1,13 @@ package org.openapitools.openapidiff.core.compare; +import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.parameters.Parameter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.IntStream; import org.openapitools.openapidiff.core.model.Changed; import org.openapitools.openapidiff.core.model.ChangedPaths; import org.openapitools.openapidiff.core.model.DiffContext; @@ -12,7 +15,7 @@ import org.openapitools.openapidiff.core.model.deferred.DeferredChanged; public class PathsDiff { - private static final String REGEX_PATH = "\\{([^/]+)}"; + private static final String REGEX_PATH = "\\{([^/{}]+)}"; private final OpenApiDiff openApiDiff; public PathsDiff(OpenApiDiff openApiDiff) { @@ -37,7 +40,7 @@ public DeferredChanged<ChangedPaths> diff( final Map<String, PathItem> left, final Map<String, PathItem> right) { DeferredBuilder<Changed> builder = new DeferredBuilder<>(); - ChangedPaths changedPaths = new ChangedPaths(left, right); + ChangedPaths changedPaths = new ChangedPaths(left, right, openApiDiff.getOptions()); changedPaths.getIncreased().putAll(right); left.keySet() @@ -50,7 +53,7 @@ public DeferredChanged<ChangedPaths> diff( .filter(item -> normalizePath(item.getKey()).equals(template)) .min( (a, b) -> { - if (methodsIntersect(a.getValue(), b.getValue())) { + if (methodsAndParametersIntersect(a.getValue(), b.getValue())) { throw new IllegalArgumentException( "Two path items have the same signature: " + template); } @@ -79,9 +82,10 @@ public DeferredChanged<ChangedPaths> diff( params.put(oldParams.get(i), newParams.get(i)); } } - DiffContext context = new DiffContext(); + DiffContext context = new DiffContext(openApiDiff.getOptions()); context.setUrl(url); context.setParameters(params); + context.setLeftAndRightUrls(url, rightUrl); builder .with(openApiDiff.getPathDiff().diff(leftPath, rightPath, context)) .ifPresent(path -> changedPaths.getChanged().put(rightUrl, path)); @@ -99,13 +103,54 @@ public static Paths valOrEmpty(Paths path) { return path; } - private static boolean methodsIntersect(PathItem a, PathItem b) { + /** + * @param a a path form the open api spec + * @param b another path from the same open api spec + * @return <code>true</code> in case both paths are of the same method AND their templated + * parameters are of the same type; <code>false</code> otherwise + */ + private static boolean methodsAndParametersIntersect(PathItem a, PathItem b) { Set<PathItem.HttpMethod> methodsA = a.readOperationsMap().keySet(); for (PathItem.HttpMethod method : b.readOperationsMap().keySet()) { if (methodsA.contains(method)) { - return true; + Operation left = a.readOperationsMap().get(method); + Operation right = b.readOperationsMap().get(method); + if (left.getParameters().size() == right.getParameters().size()) { + return parametersIntersect(left.getParameters(), right.getParameters()); + } + return false; } } return false; } + + /** + * Checks if provided parameter pairs are equal by type and format + * + * @param left parameters from the first compared method + * @param right parameters from the second compared method + * @return <code>true</code> in case each parameter pair is of the same type; <code>false</code> + * otherwise + */ + private static boolean parametersIntersect(List<Parameter> left, List<Parameter> right) { + int parametersSize = left.size(); + long intersectedParameters = + IntStream.range(0, left.size()) + .filter(i -> parametersTypeEquals(left.get(i), right.get(i))) + .count(); + return parametersSize == intersectedParameters; + } + + /** + * Checks if provided parameter pair is equal by type and format + * + * @param left parameter from the first compared method + * @param right parameter from the second compared method + * @return <code>true</code> in case parameter pair is of the same type; <code>false</code> + * otherwise + */ + private static boolean parametersTypeEquals(Parameter left, Parameter right) { + return Objects.equals(left.getSchema().getType(), right.getSchema().getType()) + && Objects.equals(left.getSchema().getFormat(), right.getSchema().getFormat()); + } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java index 822160ede..1b410818b 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java @@ -10,11 +10,13 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.XML; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import org.openapitools.openapidiff.core.compare.schemadiffresult.ArraySchemaDiffResult; import org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult; import org.openapitools.openapidiff.core.compare.schemadiffresult.SchemaDiffResult; @@ -79,23 +81,32 @@ public static SchemaDiffResult getSchemaDiffResult( } } - protected static Schema<?> resolveComposedSchema(Components components, Schema<?> schema) { + protected static Schema<?> resolveComposedSchema( + Components components, Schema<?> schema, Set<String> visitedRefs) { if (schema instanceof ComposedSchema) { ComposedSchema composedSchema = (ComposedSchema) schema; - List<Schema> allOfSchemaList = composedSchema.getAllOf(); - if (allOfSchemaList != null) { - for (Schema<?> allOfSchema : allOfSchemaList) { - allOfSchema = refPointer.resolveRef(components, allOfSchema, allOfSchema.get$ref()); - allOfSchema = resolveComposedSchema(components, allOfSchema); - schema = addSchema(schema, allOfSchema); + List<Schema> composedSchemas = new ArrayList<>(); + Optional.ofNullable(composedSchema.getAllOf()).ifPresent(composedSchemas::addAll); + Optional.ofNullable(composedSchema.getAnyOf()).ifPresent(composedSchemas::addAll); + + if (!composedSchemas.isEmpty()) { + for (Schema<?> composed : composedSchemas) { + if (composed.get$ref() == null || !visitedRefs.contains(composed.get$ref())) { + Set<String> updatedVisitedRefs = new HashSet<>(visitedRefs); + updatedVisitedRefs.add(composed.get$ref()); + composed = refPointer.resolveRef(components, composed, composed.get$ref()); + composed = resolveComposedSchema(components, composed, updatedVisitedRefs); + schema = addSchema(schema, composed); + } } composedSchema.setAllOf(null); + composedSchema.setAnyOf(null); } } return schema; } - protected static Schema<?> addSchema(Schema<?> schema, Schema<?> fromSchema) { + protected static <T, U> Schema<T> addSchema(Schema<T> schema, Schema<U> fromSchema) { if (fromSchema.getProperties() != null) { if (schema.getProperties() == null) { schema.setProperties(new LinkedHashMap<>()); @@ -154,6 +165,16 @@ protected static Schema<?> addSchema(Schema<?> schema, Schema<?> fromSchema) { } schema.getExtensions().putAll(fromSchema.getExtensions()); } + if (fromSchema instanceof ComposedSchema && schema instanceof ComposedSchema) { + ComposedSchema composedFromSchema = (ComposedSchema) fromSchema; + ComposedSchema composedSchema = (ComposedSchema) schema; + if (composedFromSchema.getOneOf() != null) { + if (composedSchema.getOneOf() == null) { + composedSchema.setOneOf(new ArrayList<>()); + } + composedSchema.getOneOf().addAll(composedFromSchema.getOneOf()); + } + } if (fromSchema.getDiscriminator() != null) { if (schema.getDiscriminator() == null) { schema.setDiscriminator(new Discriminator()); @@ -185,6 +206,9 @@ protected static Schema<?> addSchema(Schema<?> schema, Schema<?> fromSchema) { if (fromSchema.getExample() != null) { schema.setExample(fromSchema.getExample()); } + if (fromSchema.getExamples() != null) { + schema.setExamples((List<T>) fromSchema.getExamples()); + } if (fromSchema.getExternalDocs() != null) { if (schema.getExternalDocs() == null) { schema.setExternalDocs(new ExternalDocumentation()); @@ -316,8 +340,8 @@ public DeferredChanged<ChangedSchema> computeDiffForReal( left = refPointer.resolveRef(this.leftComponents, left, getSchemaRef(left)); right = refPointer.resolveRef(this.rightComponents, right, getSchemaRef(right)); - left = resolveComposedSchema(leftComponents, left); - right = resolveComposedSchema(rightComponents, right); + left = resolveComposedSchema(leftComponents, left, new HashSet<>()); + right = resolveComposedSchema(rightComponents, right, new HashSet<>()); // If type of schemas are different, just set old & new schema, set changedType to true in // SchemaDiffResult and diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java index 237e7815f..76b51a5d0 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java @@ -66,7 +66,7 @@ public DeferredChanged<ChangedSecurityRequirement> diff( DeferredBuilder<Changed> builder = new DeferredBuilder<>(); ChangedSecurityRequirement changedSecurityRequirement = - new ChangedSecurityRequirement(left, right != null ? getCopy(right) : null); + new ChangedSecurityRequirement(left, right != null ? getCopy(right) : null, context); SecurityRequirement leftRequirement = left == null ? new SecurityRequirement() : left; SecurityRequirement rightRequirement = right == null ? new SecurityRequirement() : right; diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java index 291dd5c65..324bf0017 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java @@ -88,7 +88,7 @@ protected DeferredChanged<ChangedSecurityRequirements> diff( right = right == null ? new ArrayList<>() : getCopy(right); ChangedSecurityRequirements changedSecurityRequirements = - new ChangedSecurityRequirements(left, right); + new ChangedSecurityRequirements(left, right, context); for (SecurityRequirement leftSecurity : left) { Optional<SecurityRequirement> rightSecOpt = contains(right, leftSecurity); diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java index a27edb374..f68d638d3 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java @@ -51,8 +51,8 @@ public DeferredChanged<ChangedSecurityScheme> diff( changedSecuritySchemeOptional -> { ChangedSecurityScheme changedSecurityScheme = changedSecuritySchemeOptional.orElse( - new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme)); - changedSecurityScheme = getCopyWithoutScopes(changedSecurityScheme); + new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme, context)); + changedSecurityScheme = getCopyWithoutScopes(changedSecurityScheme, context); if (changedSecurityScheme != null && leftSecurityScheme.getType() == SecurityScheme.Type.OAUTH2) { @@ -71,7 +71,7 @@ protected DeferredChanged<ChangedSecurityScheme> computeDiff( SecurityScheme rightSecurityScheme, DiffContext context) { ChangedSecurityScheme changedSecurityScheme = - new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme); + new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme, context); openApiDiff .getMetadataDiff() @@ -87,7 +87,7 @@ protected DeferredChanged<ChangedSecurityScheme> computeDiff( case OAUTH2: openApiDiff .getOAuthFlowsDiff() - .diff(leftSecurityScheme.getFlows(), rightSecurityScheme.getFlows()) + .diff(leftSecurityScheme.getFlows(), rightSecurityScheme.getFlows(), context) .ifPresent(changedSecurityScheme::setOAuthFlows); break; @@ -114,9 +114,10 @@ protected DeferredChanged<ChangedSecurityScheme> computeDiff( return new RealizedChanged<>(changedSecurityScheme); } - private ChangedSecurityScheme getCopyWithoutScopes(ChangedSecurityScheme original) { + private ChangedSecurityScheme getCopyWithoutScopes( + ChangedSecurityScheme original, DiffContext context) { return new ChangedSecurityScheme( - original.getOldSecurityScheme(), original.getNewSecurityScheme()) + original.getOldSecurityScheme(), original.getNewSecurityScheme(), context) .setChangedType(original.isChangedType()) .setChangedIn(original.isChangedIn()) .setChangedScheme(original.isChangedScheme()) diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java index e76aa0155..2f86318eb 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java @@ -12,13 +12,13 @@ import org.apache.commons.collections4.CollectionUtils; import org.openapitools.openapidiff.core.compare.MapKeyDiff; import org.openapitools.openapidiff.core.compare.OpenApiDiff; -import org.openapitools.openapidiff.core.model.ChangedOneOfSchema; import org.openapitools.openapidiff.core.model.ChangedSchema; import org.openapitools.openapidiff.core.model.DiffContext; import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder; import org.openapitools.openapidiff.core.model.deferred.DeferredChanged; import org.openapitools.openapidiff.core.model.deferred.RealizedChanged; import org.openapitools.openapidiff.core.model.deferred.RecursiveSchemaSet; +import org.openapitools.openapidiff.core.model.schema.ChangedOneOfSchema; import org.openapitools.openapidiff.core.utils.RefPointer; import org.openapitools.openapidiff.core.utils.RefType; diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java index 540188ecb..32cb5bee9 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java @@ -11,12 +11,16 @@ import org.openapitools.openapidiff.core.compare.OpenApiDiff; import org.openapitools.openapidiff.core.model.Change; import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.ChangedExample; +import org.openapitools.openapidiff.core.model.ChangedExamples; import org.openapitools.openapidiff.core.model.ChangedSchema; import org.openapitools.openapidiff.core.model.DiffContext; import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder; import org.openapitools.openapidiff.core.model.deferred.DeferredChanged; import org.openapitools.openapidiff.core.model.deferred.RecursiveSchemaSet; import org.openapitools.openapidiff.core.model.schema.*; +import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems; +import org.openapitools.openapidiff.core.model.schema.ChangedMinItems; public class SchemaDiffResult { protected ChangedSchema changedSchema; @@ -48,9 +52,7 @@ public <V extends Schema<X>, X> DeferredChanged<ChangedSchema> diff( .setContext(context) .setOldSchema(left) .setNewSchema(right) - .setChangeDeprecated( - !Boolean.TRUE.equals(left.getDeprecated()) - && Boolean.TRUE.equals(right.getDeprecated())) + .setChangeDeprecated(!Objects.equals(left.getDeprecated(), right.getDeprecated())) .setChangeTitle(!Objects.equals(left.getTitle(), right.getTitle())) .setRequired( ListDiff.diff(new ChangedRequired(left.getRequired(), right.getRequired(), context))) @@ -59,7 +61,29 @@ public <V extends Schema<X>, X> DeferredChanged<ChangedSchema> diff( .setChangeFormat(!Objects.equals(left.getFormat(), right.getFormat())) .setReadOnly(new ChangedReadOnly(left.getReadOnly(), right.getReadOnly(), context)) .setWriteOnly(new ChangedWriteOnly(left.getWriteOnly(), right.getWriteOnly(), context)) - .setMaxLength(new ChangedMaxLength(left.getMaxLength(), right.getMaxLength(), context)); + .setMaxLength(new ChangedMaxLength(left.getMaxLength(), right.getMaxLength(), context)) + .setNumericRange( + new ChangedNumericRange( + left.getMinimum(), + right.getMinimum(), + left.getMaximum(), + right.getMaximum(), + left.getExclusiveMinimum(), + right.getExclusiveMinimum(), + left.getExclusiveMaximum(), + right.getExclusiveMaximum(), + context)) + .setMultipleOf(new ChangedMultipleOf(left.getMultipleOf(), right.getMultipleOf())) + .setNullable(new ChangedNullable(left.getNullable(), right.getNullable())) + .setUniqueItems(new ChangedUniqueItems(left.getUniqueItems(), right.getUniqueItems())) + .setExamples(new ChangedExamples(left.getExamples(), right.getExamples())) + .setExample(new ChangedExample(left.getExample(), right.getExample())) + .setMaxItems(new ChangedMaxItems(left.getMaxItems(), right.getMaxItems(), context)) + .setMinItems(new ChangedMinItems(left.getMinItems(), right.getMinItems(), context)) + .setMaxProperties( + new ChangedMaxProperties(left.getMaxProperties(), right.getMaxProperties(), context)) + .setMinProperties( + new ChangedMinProperties(left.getMinProperties(), right.getMinProperties(), context)); builder .with( openApiDiff diff --git a/core/src/main/java/org/openapitools/openapidiff/core/exception/RendererException.java b/core/src/main/java/org/openapitools/openapidiff/core/exception/RendererException.java new file mode 100644 index 000000000..d7dc24447 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/exception/RendererException.java @@ -0,0 +1,12 @@ +package org.openapitools.openapidiff.core.exception; + +public class RendererException extends RuntimeException { + + public RendererException(Throwable cause) { + super(cause); + } + + public RendererException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java b/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java new file mode 100644 index 000000000..f05a72f75 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java @@ -0,0 +1,82 @@ +package org.openapitools.openapidiff.core.model; + +import static org.apache.commons.lang3.ArrayUtils.isEmpty; + +import org.apache.commons.configuration2.Configuration; + +// Properties to enable/disable backward incompatibility checks +// Note: order is not programmatically significant but keep alphabetical for maintainability +public enum BackwardIncompatibleProp { + EXTENSION_CONTENT_TYPES_DECREASED("incompatible.extension.content.types.decreased", false), + EXTENSION_CONTENT_TYPE_DELETED("incompatible.extension.content.type.%s.deleted", false), + OPENAPI_ENDPOINTS_DECREASED("incompatible.openapi.endpoints.decreased", true), + REQUEST_BODY_REQUIRED_INCREASED("incompatible.request.body.increased", true), + REQUEST_CONTENT_DECREASED("incompatible.request.content.decreased", true), + REQUEST_ENUM_DECREASED("incompatible.request.enum.decreased", true), + REQUEST_MAX_LENGTH_DECREASED("incompatible.request.max.length.decreased", true), + REQUEST_NUMERIC_RANGE_DECREASED("incompatible.request.numeric.range.decreased", true), + REQUEST_ONEOF_DECREASED("incompatible.request.oneof.decreased", true), + REQUEST_PARAM_ALLOWEMPTY_DECREASED("incompatible.request.param.allowempty.decreased", true), + REQUEST_PARAM_EXPLODE_CHANGED("incompatible.request.param.explode.changed", true), + REQUEST_PARAM_STYLE_CHANGED("incompatible.request.param.style.changed", true), + REQUEST_PARAMS_DECREASED("incompatible.request.params.decreased", true), + REQUEST_PARAMS_REQUIRED_INCREASED("incompatible.request.params.required.increased", true), + REQUEST_READONLY_INCREASED("incompatible.request.readonly.increased", true), + REQUEST_READONLY_REQUIRED_DECREASED("incompatible.request.readonly.required.decreased", true), + REQUEST_REQUIRED_INCREASED("incompatible.request.required.increased", true), + RESPONSE_CONTENT_DECREASED("incompatible.response.content.decreased", true), + RESPONSE_ENUM_INCREASED("incompatible.response.enum.increased", true), + RESPONSE_HEADER_EXPLODE_CHANGED("incompatible.response.header.explode.changed", true), + RESPONSE_HEADER_REQUIRED_DECREASED("incompatible.response.header.required.decreased", true), + RESPONSE_HEADER_REQUIRED_INCREASED("incompatible.response.header.required.increased", true), + RESPONSE_HEADERS_DECREASED("incompatible.response.headers.decreased", true), + RESPONSE_MAX_LENGTH_INCREASED("incompatible.response.max.length.increased", true), + RESPONSE_NUMERIC_RANGE_INCREASED("incompatible.response.numeric.range.increased", false), + RESPONSE_ONEOF_INCREASED("incompatible.response.oneof.increased", true), + RESPONSE_REQUIRED_DECREASED("incompatible.response.required.decreased", true), + RESPONSE_RESPONSES_DECREASED("incompatible.response.responses.decreased", true), + RESPONSE_WRITEONLY_INCREASED("incompatible.response.writeonly.increased", true), + RESPONSE_WRITEONLY_REQUIRED_DECREASED("incompatible.response.writeonly.required.decreased", true), + SECURITY_REQUIREMENT_SCHEMES_INCREASED( + "incompatible.security.requirement.schemes.increased", true), + SECURITY_REQUIREMENTS_DECREASED("incompatible.security.requirements.decreased", true), + SECURITY_SCHEME_BEARER_FORMAT_CHANGED("incompatible.security.scheme.bearer.format.changed", true), + SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED( + "incompatible.security.scheme.oauth2.auth.url.changed", true), + SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED( + "incompatible.security.scheme.oauth2.refresh.url.changed", true), + SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED( + "incompatible.security.scheme.oauth2.token.url.changed", true), + SECURITY_SCHEME_OPENIDCONNECT_URL_CHANGED( + "incompatible.security.scheme.openidconnect.url.changed", true), + SECURITY_SCHEME_SCHEME_CHANGED("incompatible.security.scheme.scheme.changed", true), + SECURITY_SCHEME_SCOPES_INCREASED("incompatible.security.scheme.scopes.increased", true), + SCHEMA_DISCRIMINATOR_CHANGED("incompatible.schema.discriminator.changed", true), + SCHEMA_TYPE_CHANGED("incompatible.schema.type.changed", true), + ; + + private final String propertyName; + private final boolean enabledByDefault; + + BackwardIncompatibleProp(String propertyName, boolean enabledByDefault) { + this.propertyName = propertyName; + this.enabledByDefault = enabledByDefault; + } + + public String getPropertyName() { + return propertyName; + } + + public boolean isEnabledByDefault() { + return enabledByDefault; + } + + public boolean enabled(DiffContext context, Object... formatArgs) { + return enabled(context.getConfig(), formatArgs); + } + + public boolean enabled(Configuration cfg, Object... formatArgs) { + String propName = isEmpty(formatArgs) ? propertyName : String.format(propertyName, formatArgs); + return cfg.getBoolean(propName, enabledByDefault); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java index e496f7a71..0b12a0ead 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_RESPONSES_DECREASED; + import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import java.util.LinkedHashMap; @@ -39,10 +41,12 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (!increased.isEmpty() && missing.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missing.isEmpty()) { + if (RESPONSE_RESPONSES_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public ApiResponses getOldApiResponses() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java index 5bc6ddf2a..472803ffd 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_CONTENT_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_CONTENT_DECREASED; + import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; import java.util.*; @@ -31,10 +34,15 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (context.isRequest() && missing.isEmpty() || context.isResponse() && increased.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missing.isEmpty()) { + if (context.isRequest() && REQUEST_CONTENT_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + if (context.isResponse() && RESPONSE_CONTENT_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public Content getOldContent() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExample.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExample.java new file mode 100644 index 000000000..4e540eca3 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExample.java @@ -0,0 +1,66 @@ +package org.openapitools.openapidiff.core.model; + +import java.util.Objects; + +public class ChangedExample implements Changed { + + private Object leftExample; + private Object rightExample; + + public ChangedExample(Object leftExample, Object rightExample) { + this.leftExample = leftExample; + this.rightExample = rightExample; + } + + public Object getLeftExample() { + return leftExample; + } + + public void setLeftExample(Object leftExample) { + this.leftExample = leftExample; + } + + public Object getRightExample() { + return rightExample; + } + + public void setRightExample(Object rightExample) { + this.rightExample = rightExample; + } + + @Override + public DiffResult isChanged() { + if (!Objects.equals(leftExample, rightExample)) { + return DiffResult.METADATA; + } + return DiffResult.NO_CHANGES; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + ChangedExample that = (ChangedExample) object; + return Objects.equals(leftExample, that.leftExample) + && Objects.equals(rightExample, that.rightExample); + } + + @Override + public int hashCode() { + return Objects.hash(leftExample, rightExample); + } + + @Override + public String toString() { + return "ChangedExample{" + + "leftExample=" + + leftExample + + ", rightExample=" + + rightExample + + '}'; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExamples.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExamples.java new file mode 100644 index 000000000..c1a00bbbc --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExamples.java @@ -0,0 +1,66 @@ +package org.openapitools.openapidiff.core.model; + +import java.util.Objects; + +public class ChangedExamples implements Changed { + + private Object leftExamples; + private Object rightExamples; + + public ChangedExamples(Object leftExamples, Object rightExamples) { + this.leftExamples = leftExamples; + this.rightExamples = rightExamples; + } + + public Object getLeftExamples() { + return leftExamples; + } + + public void setLeftExamples(Object leftExamples) { + this.leftExamples = leftExamples; + } + + public Object getRightExamples() { + return rightExamples; + } + + public void setRightExamples(Object rightExamples) { + this.rightExamples = rightExamples; + } + + @Override + public DiffResult isChanged() { + if (!Objects.equals(leftExamples, rightExamples)) { + return DiffResult.METADATA; + } + return DiffResult.NO_CHANGES; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + ChangedExamples that = (ChangedExamples) object; + return Objects.equals(leftExamples, that.leftExamples) + && Objects.equals(rightExamples, that.rightExamples); + } + + @Override + public int hashCode() { + return Objects.hash(leftExamples, rightExamples); + } + + @Override + public String toString() { + return "ChangedExamples{" + + ", leftExamples=" + + leftExamples + + ", rightExamples=" + + rightExamples + + '}'; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java index cde3b1511..f189b3e8c 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.EXTENSION_CONTENT_TYPES_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.EXTENSION_CONTENT_TYPE_DELETED; + import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -32,7 +35,20 @@ public List<Changed> getChangedElements() { @Override public DiffResult isCoreChanged() { - return DiffResult.NO_CHANGES; + if (increased.isEmpty() && missing.isEmpty()) { + return DiffResult.NO_CHANGES; + } + if (!missing.isEmpty()) { + if (EXTENSION_CONTENT_TYPES_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + for (String key : missing.keySet()) { + if (EXTENSION_CONTENT_TYPE_DELETED.enabled(context, key)) { + return DiffResult.INCOMPATIBLE; + } + } + } + return DiffResult.COMPATIBLE; } public Map<String, Object> getOldExtensions() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java index 8ec56568f..0c9e3384f 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java @@ -1,5 +1,9 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_EXPLODE_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_INCREASED; + import io.swagger.v3.oas.models.headers.Header; import java.util.Arrays; import java.util.List; @@ -14,6 +18,8 @@ public class ChangedHeader implements ComposedChanged { private boolean style; private boolean explode; private ChangedMetadata description; + private ChangedExamples examples; + private ChangedExample example; private ChangedSchema schema; private ChangedContent content; private ChangedExtensions extensions; @@ -26,7 +32,7 @@ public ChangedHeader(Header oldHeader, Header newHeader, DiffContext context) { @Override public List<Changed> getChangedElements() { - return Arrays.asList(description, schema, content, extensions); + return Arrays.asList(description, example, examples, schema, content, extensions); } @Override @@ -34,10 +40,30 @@ public DiffResult isCoreChanged() { if (!required && !deprecated && !style && !explode) { return DiffResult.NO_CHANGES; } - if (!required && !style && !explode) { - return DiffResult.COMPATIBLE; + if (explode) { + if (RESPONSE_HEADER_EXPLODE_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (required) { + boolean requiredOld = oldHeader.getRequired() != null ? oldHeader.getRequired() : false; + boolean requiredNew = newHeader.getRequired() != null ? newHeader.getRequired() : false; + if (requiredOld && !requiredNew) { + if (RESPONSE_HEADER_REQUIRED_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (!requiredOld && requiredNew) { + // TODO: Document why desired or remove support. Client will just ignore new header? + if (RESPONSE_HEADER_REQUIRED_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } } - return DiffResult.INCOMPATIBLE; + if (style) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; } public Header getOldHeader() { @@ -72,6 +98,14 @@ public ChangedMetadata getDescription() { return this.description; } + public ChangedExamples getExamples() { + return this.examples; + } + + public ChangedExample getExample() { + return this.example; + } + public ChangedSchema getSchema() { return this.schema; } @@ -109,6 +143,16 @@ public ChangedHeader setDescription(final ChangedMetadata description) { return this; } + public ChangedHeader setExamples(ChangedExamples examples) { + this.examples = examples; + return this; + } + + public ChangedHeader setExample(ChangedExample example) { + this.example = example; + return this; + } + public ChangedHeader setSchema(final ChangedSchema schema) { this.schema = schema; return this; @@ -137,6 +181,8 @@ public boolean equals(Object o) { && Objects.equals(newHeader, that.newHeader) && Objects.equals(context, that.context) && Objects.equals(description, that.description) + && Objects.equals(examples, that.examples) + && Objects.equals(example, that.example) && Objects.equals(schema, that.schema) && Objects.equals(content, that.content) && Objects.equals(extensions, that.extensions); @@ -153,6 +199,8 @@ public int hashCode() { style, explode, description, + examples, + example, schema, content, extensions); @@ -176,6 +224,10 @@ public java.lang.String toString() { + this.isExplode() + ", description=" + this.getDescription() + + ", examples=" + + this.getExamples() + + ", example=" + + this.getExample() + ", schema=" + this.getSchema() + ", content=" diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java index 451e94f1e..dd228da0f 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADERS_DECREASED; + import io.swagger.v3.oas.models.headers.Header; import java.util.ArrayList; import java.util.List; @@ -31,10 +33,12 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (missing.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missing.isEmpty()) { + if (RESPONSE_HEADERS_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public Map<String, Header> getOldHeaders() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java index 7136dea04..43f9a443d 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java @@ -1,7 +1,7 @@ package org.openapitools.openapidiff.core.model; import io.swagger.v3.oas.models.media.Schema; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -10,6 +10,8 @@ public class ChangedMediaType implements ComposedChanged { private final Schema newSchema; private final DiffContext context; private ChangedSchema schema; + private ChangedExamples examples; + private ChangedExample example; public ChangedMediaType(Schema oldSchema, Schema newSchema, DiffContext context) { this.oldSchema = oldSchema; @@ -19,7 +21,7 @@ public ChangedMediaType(Schema oldSchema, Schema newSchema, DiffContext context) @Override public List<Changed> getChangedElements() { - return Collections.singletonList(schema); + return Arrays.asList(schema, examples, example); } @Override @@ -43,11 +45,29 @@ public ChangedSchema getSchema() { return this.schema; } + public ChangedExamples getExamples() { + return this.examples; + } + + public ChangedExample getExample() { + return this.example; + } + public ChangedMediaType setSchema(final ChangedSchema schema) { this.schema = schema; return this; } + public ChangedMediaType setExamples(final ChangedExamples examples) { + this.examples = examples; + return this; + } + + public ChangedMediaType setExample(final ChangedExample example) { + this.example = example; + return this; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -56,12 +76,14 @@ public boolean equals(Object o) { return Objects.equals(oldSchema, that.oldSchema) && Objects.equals(newSchema, that.newSchema) && Objects.equals(context, that.context) - && Objects.equals(schema, that.schema); + && Objects.equals(schema, that.schema) + && Objects.equals(examples, that.examples) + && Objects.equals(example, that.example); } @Override public int hashCode() { - return Objects.hash(oldSchema, newSchema, context, schema); + return Objects.hash(oldSchema, newSchema, context, schema, examples, example); } @java.lang.Override @@ -74,6 +96,10 @@ public java.lang.String toString() { + this.getContext() + ", schema=" + this.getSchema() + + ", examples=" + + this.getExamples() + + ", example=" + + this.getExample() + ")"; } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java index 5409d1d99..b6179e9be 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java @@ -1,5 +1,9 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED; + import io.swagger.v3.oas.models.security.OAuthFlow; import java.util.Collections; import java.util.List; @@ -8,14 +12,16 @@ public class ChangedOAuthFlow implements ComposedChanged { private OAuthFlow oldOAuthFlow; private OAuthFlow newOAuthFlow; + private final DiffContext context; private boolean authorizationUrl; private boolean tokenUrl; private boolean refreshUrl; private ChangedExtensions extensions; - public ChangedOAuthFlow(OAuthFlow oldOAuthFlow, OAuthFlow newOAuthFlow) { + public ChangedOAuthFlow(OAuthFlow oldOAuthFlow, OAuthFlow newOAuthFlow, DiffContext context) { this.oldOAuthFlow = oldOAuthFlow; this.newOAuthFlow = newOAuthFlow; + this.context = context; } @Override @@ -25,10 +31,25 @@ public List<Changed> getChangedElements() { @Override public DiffResult isCoreChanged() { - if (authorizationUrl || tokenUrl || refreshUrl) { - return DiffResult.INCOMPATIBLE; + if (!authorizationUrl && !tokenUrl && !refreshUrl) { + return DiffResult.NO_CHANGES; + } + if (authorizationUrl) { + if (SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (refreshUrl) { + if (SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (tokenUrl) { + if (SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.NO_CHANGES; + return DiffResult.COMPATIBLE; } public OAuthFlow getOldOAuthFlow() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java index a1c796ac5..d0f23ca16 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java @@ -1,21 +1,30 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.models.OpenAPI; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; import org.openapitools.openapidiff.core.utils.EndpointUtils; public class ChangedOpenApi implements ComposedChanged { - private OpenAPI oldSpecOpenApi; - private OpenAPI newSpecOpenApi; + private final OpenApiDiffOptions options; + @JsonIgnore private OpenAPI oldSpecOpenApi; + @JsonIgnore private OpenAPI newSpecOpenApi; private List<Endpoint> newEndpoints; private List<Endpoint> missingEndpoints; private List<ChangedOperation> changedOperations; private List<ChangedSchema> changedSchemas; private ChangedExtensions changedExtensions; + public ChangedOpenApi(OpenApiDiffOptions options) { + this.options = options; + } + public List<Endpoint> getDeprecatedEndpoints() { return changedOperations.stream() .filter(ChangedOperation::isDeprecated) @@ -39,10 +48,12 @@ public DiffResult isCoreChanged() { if (newEndpoints.isEmpty() && missingEndpoints.isEmpty()) { return DiffResult.NO_CHANGES; } - if (missingEndpoints.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missingEndpoints.isEmpty()) { + if (OPENAPI_ENDPOINTS_DECREASED.enabled(options.getConfig())) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public OpenAPI getOldSpecOpenApi() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java index 0da0516fc..2a6cc773a 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java @@ -64,6 +64,10 @@ public DiffResult resultRequestBody() { return requestBody == null ? DiffResult.NO_CHANGES : requestBody.isChanged(); } + public DiffResult resultSecurityRequirements() { + return securityRequirements == null ? DiffResult.NO_CHANGES : securityRequirements.isChanged(); + } + public Operation getOldOperation() { return this.oldOperation; } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java index 998570186..4b8208e24 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.*; + import io.swagger.v3.oas.models.parameters.Parameter; import java.util.Arrays; import java.util.List; @@ -17,6 +19,8 @@ public class ChangedParameter implements ComposedChanged { private boolean changeExplode; private boolean changeAllowEmptyValue; private ChangedMetadata description; + private ChangedExamples examples; + private ChangedExample example; private ChangedSchema schema; private ChangedContent content; private ChangedExtensions extensions; @@ -29,7 +33,7 @@ public ChangedParameter(String name, String in, DiffContext context) { @Override public List<Changed> getChangedElements() { - return Arrays.asList(description, schema, content, extensions); + return Arrays.asList(description, examples, example, schema, content, extensions); } @Override @@ -41,13 +45,27 @@ public DiffResult isCoreChanged() { && !changeExplode) { return DiffResult.NO_CHANGES; } - if ((!changeRequired || Boolean.TRUE.equals(oldParameter.getRequired())) - && (!changeAllowEmptyValue || Boolean.TRUE.equals(newParameter.getAllowEmptyValue())) - && !changeStyle - && !changeExplode) { - return DiffResult.COMPATIBLE; + if (changeAllowEmptyValue && !Boolean.TRUE.equals(newParameter.getAllowEmptyValue())) { + if (REQUEST_PARAM_ALLOWEMPTY_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (changeExplode) { + if (REQUEST_PARAM_EXPLODE_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + if (changeRequired && !Boolean.TRUE.equals(oldParameter.getRequired())) { + if (REQUEST_PARAMS_REQUIRED_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (changeStyle) { + if (REQUEST_PARAM_STYLE_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + return DiffResult.COMPATIBLE; } public DiffContext getContext() { @@ -94,6 +112,14 @@ public ChangedMetadata getDescription() { return this.description; } + public ChangedExamples getExamples() { + return this.examples; + } + + public ChangedExample getExample() { + return this.example; + } + public ChangedSchema getSchema() { return this.schema; } @@ -156,6 +182,16 @@ public ChangedParameter setDescription(final ChangedMetadata description) { return this; } + public ChangedParameter setExamples(final ChangedExamples examples) { + this.examples = examples; + return this; + } + + public ChangedParameter setExample(final ChangedExample example) { + this.example = example; + return this; + } + public ChangedParameter setSchema(final ChangedSchema schema) { this.schema = schema; return this; @@ -187,6 +223,8 @@ public boolean equals(Object o) { && Objects.equals(name, that.name) && Objects.equals(in, that.in) && Objects.equals(description, that.description) + && Objects.equals(examples, that.examples) + && Objects.equals(example, that.example) && Objects.equals(schema, that.schema) && Objects.equals(content, that.content) && Objects.equals(extensions, that.extensions); @@ -206,6 +244,8 @@ public int hashCode() { changeExplode, changeAllowEmptyValue, description, + examples, + example, schema, content, extensions); @@ -235,6 +275,10 @@ public java.lang.String toString() { + this.isChangeAllowEmptyValue() + ", description=" + this.getDescription() + + ", examples=" + + this.getExamples() + + ", example=" + + this.getExample() + ", schema=" + this.getSchema() + ", content=" diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java index 2b119bf80..86dc145fd 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_REQUIRED_INCREASED; + import io.swagger.v3.oas.models.parameters.Parameter; import java.util.ArrayList; import java.util.List; @@ -33,11 +36,17 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (increased.stream().noneMatch(p -> p.getRequired() != null && p.getRequired()) - && missing.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missing.isEmpty()) { + if (REQUEST_PARAMS_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (increased.stream().anyMatch(p -> p.getRequired() != null && p.getRequired())) { + if (REQUEST_PARAMS_REQUIRED_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public List<Parameter> getOldParameterList() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java index e9fa4093a..2ba9830e7 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import java.util.*; @@ -36,10 +38,12 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (missing.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missing.isEmpty()) { + if (OPENAPI_ENDPOINTS_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public String getPathUrl() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java index 96af52785..5e032b498 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java @@ -1,16 +1,24 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + import io.swagger.v3.oas.models.PathItem; import java.util.*; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; public class ChangedPaths implements ComposedChanged { + private final OpenApiDiffOptions options; private final Map<String, PathItem> oldPathMap; private final Map<String, PathItem> newPathMap; private Map<String, PathItem> increased; private Map<String, PathItem> missing; private Map<String, ChangedPath> changed; - public ChangedPaths(Map<String, PathItem> oldPathMap, Map<String, PathItem> newPathMap) { + public ChangedPaths( + Map<String, PathItem> oldPathMap, + Map<String, PathItem> newPathMap, + OpenApiDiffOptions options) { + this.options = options; this.oldPathMap = oldPathMap; this.newPathMap = newPathMap; this.increased = new LinkedHashMap<>(); @@ -28,10 +36,12 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (missing.isEmpty()) { - return DiffResult.COMPATIBLE; + if (!missing.isEmpty()) { + if (OPENAPI_ENDPOINTS_DECREASED.enabled(options.getConfig())) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public Map<String, PathItem> getOldPathMap() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java index 83fadd407..48cc4cf0e 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + import io.swagger.v3.oas.models.parameters.RequestBody; import java.util.Arrays; import java.util.List; @@ -31,7 +33,10 @@ public DiffResult isCoreChanged() { if (!changeRequired) { return DiffResult.NO_CHANGES; } - return DiffResult.INCOMPATIBLE; + if (OPENAPI_ENDPOINTS_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; } public RequestBody getOldRequestBody() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java index c6289f17d..3779ee853 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java @@ -1,14 +1,26 @@ package org.openapitools.openapidiff.core.model; -import io.swagger.v3.oas.models.PathItem; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_DISCRIMINATOR_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_TYPE_CHANGED; + import io.swagger.v3.oas.models.media.Schema; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import org.openapitools.openapidiff.core.model.schema.ChangedEnum; +import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems; import org.openapitools.openapidiff.core.model.schema.ChangedMaxLength; +import org.openapitools.openapidiff.core.model.schema.ChangedMaxProperties; +import org.openapitools.openapidiff.core.model.schema.ChangedMinItems; +import org.openapitools.openapidiff.core.model.schema.ChangedMinProperties; +import org.openapitools.openapidiff.core.model.schema.ChangedMultipleOf; +import org.openapitools.openapidiff.core.model.schema.ChangedNullable; +import org.openapitools.openapidiff.core.model.schema.ChangedNumericRange; +import org.openapitools.openapidiff.core.model.schema.ChangedOneOfSchema; import org.openapitools.openapidiff.core.model.schema.ChangedReadOnly; import org.openapitools.openapidiff.core.model.schema.ChangedRequired; +import org.openapitools.openapidiff.core.model.schema.ChangedUniqueItems; import org.openapitools.openapidiff.core.model.schema.ChangedWriteOnly; public class ChangedSchema implements ComposedChanged { @@ -21,6 +33,8 @@ public class ChangedSchema implements ComposedChanged { protected Map<String, Schema<?>> missingProperties; protected boolean changeDeprecated; protected ChangedMetadata description; + protected ChangedExamples examples; + protected ChangedExample example; protected boolean changeTitle; protected ChangedRequired required; protected boolean changeDefault; @@ -30,6 +44,14 @@ public class ChangedSchema implements ComposedChanged { protected ChangedWriteOnly writeOnly; protected boolean changedType; protected ChangedMaxLength maxLength; + protected ChangedNumericRange numericRange; + protected ChangedMultipleOf multipleOf; + protected ChangedMaxItems maxItems; + protected ChangedMinItems minItems; + protected ChangedMaxProperties maxProperties; + protected ChangedMinProperties minProperties; + protected ChangedNullable nullable; + protected ChangedUniqueItems uniqueItems; protected boolean discriminatorPropertyChanged; protected ChangedSchema items; protected ChangedOneOfSchema oneOfSchema; @@ -101,6 +123,8 @@ public List<Changed> getChangedElements() { changedProperties.values().stream(), Stream.of( description, + examples, + example, readOnly, writeOnly, items, @@ -109,6 +133,14 @@ public List<Changed> getChangedElements() { enumeration, required, maxLength, + numericRange, + multipleOf, + maxItems, + minItems, + maxProperties, + minProperties, + nullable, + uniqueItems, extensions)) .collect(Collectors.toList()); } @@ -130,6 +162,7 @@ private DiffResult calculateCoreChanged() { if (!changedType && (oldSchema == null && newSchema == null || oldSchema != null && newSchema != null) && !changeFormat + && !changeDefault && increasedProperties.isEmpty() && missingProperties.isEmpty() && changedProperties.values().isEmpty() @@ -137,22 +170,50 @@ private DiffResult calculateCoreChanged() { && !discriminatorPropertyChanged) { return DiffResult.NO_CHANGES; } - boolean compatibleForResponse = - missingProperties.isEmpty() && (oldSchema == null || newSchema != null); - if ((context.isRequest() && compatibleForRequest() - || context.isResponse() && compatibleForResponse) - && !changedType - && !discriminatorPropertyChanged) { - return DiffResult.COMPATIBLE; + if (changedType) { + if (SCHEMA_TYPE_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (discriminatorPropertyChanged) { + if (SCHEMA_DISCRIMINATOR_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + + if (!compatibleForRequest() || !compatibleForResponse()) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; } private boolean compatibleForRequest() { - if (PathItem.HttpMethod.PUT.equals(context.getMethod()) && !increasedProperties.isEmpty()) { - return false; + if (context.isRequest()) { + if (oldSchema == null && newSchema != null) { + // TODO: dead code? If not, create test. + return false; + } + } + return true; + } + + private boolean compatibleForResponse() { + if (context.isResponse()) { + if (oldSchema != null) { + if (newSchema == null) { + // TODO: dead code? If not, create test. + return false; + } + if (oldSchema.getRequired() != null + && missingProperties.keySet().stream() + .anyMatch(prop -> oldSchema.getRequired().contains(prop))) { + if (RESPONSE_REQUIRED_DECREASED.enabled(context)) { + return false; + } + } + } } - return (oldSchema != null || newSchema == null); + return true; } public DiffContext getContext() { @@ -187,6 +248,14 @@ public boolean isChangeDeprecated() { return this.changeDeprecated; } + public ChangedExamples getExamples() { + return this.examples; + } + + public ChangedExample getExample() { + return this.example; + } + public ChangedMetadata getDescription() { return this.description; } @@ -227,6 +296,30 @@ public ChangedMaxLength getMaxLength() { return this.maxLength; } + public ChangedNumericRange getNumericRange() { + return this.numericRange; + } + + public ChangedMultipleOf getMultipleOf() { + return this.multipleOf; + } + + public ChangedMaxItems getMaxItems() { + return this.maxItems; + } + + public ChangedMinItems getMinItems() { + return this.minItems; + } + + public ChangedNullable getNullable() { + return this.nullable; + } + + public ChangedUniqueItems getUniqueItems() { + return uniqueItems; + } + public boolean isDiscriminatorPropertyChanged() { return this.discriminatorPropertyChanged; } @@ -247,6 +340,14 @@ public ChangedExtensions getExtensions() { return this.extensions; } + public ChangedMaxProperties getMaxProperties() { + return this.maxProperties; + } + + public ChangedMinProperties getMinProperties() { + return this.minProperties; + } + public ChangedSchema setContext(final DiffContext context) { this.context = context; return this; @@ -297,6 +398,18 @@ public ChangedSchema setDescription(final ChangedMetadata description) { return this; } + public ChangedSchema setExamples(final ChangedExamples examples) { + clearChangedCache(); + this.examples = examples; + return this; + } + + public ChangedSchema setExample(final ChangedExample example) { + clearChangedCache(); + this.example = example; + return this; + } + public ChangedSchema setChangeTitle(final boolean changeTitle) { clearChangedCache(); this.changeTitle = changeTitle; @@ -351,6 +464,42 @@ public ChangedSchema setMaxLength(final ChangedMaxLength maxLength) { return this; } + public ChangedSchema setNumericRange(final ChangedNumericRange numericRange) { + clearChangedCache(); + this.numericRange = numericRange; + return this; + } + + public ChangedSchema setMultipleOf(final ChangedMultipleOf multipleOf) { + clearChangedCache(); + this.multipleOf = multipleOf; + return this; + } + + public ChangedSchema setMaxItems(final ChangedMaxItems maxItems) { + clearChangedCache(); + this.maxItems = maxItems; + return this; + } + + public ChangedSchema setMinItems(final ChangedMinItems minItems) { + clearChangedCache(); + this.minItems = minItems; + return this; + } + + public ChangedSchema setNullable(final ChangedNullable nullable) { + clearChangedCache(); + this.nullable = nullable; + return this; + } + + public ChangedSchema setUniqueItems(ChangedUniqueItems uniqueItems) { + clearChangedCache(); + this.uniqueItems = uniqueItems; + return this; + } + public ChangedSchema setDiscriminatorPropertyChanged(final boolean discriminatorPropertyChanged) { clearChangedCache(); this.discriminatorPropertyChanged = discriminatorPropertyChanged; @@ -381,6 +530,18 @@ public ChangedSchema setExtensions(final ChangedExtensions extensions) { return this; } + public ChangedSchema setMaxProperties(final ChangedMaxProperties maxProperties) { + clearChangedCache(); + this.maxProperties = maxProperties; + return this; + } + + public ChangedSchema setMinProperties(final ChangedMinProperties minProperties) { + clearChangedCache(); + this.minProperties = minProperties; + return this; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -400,15 +561,25 @@ public boolean equals(Object o) { && Objects.equals(increasedProperties, that.increasedProperties) && Objects.equals(missingProperties, that.missingProperties) && Objects.equals(description, that.description) + && Objects.equals(examples, that.examples) + && Objects.equals(example, that.example) && Objects.equals(required, that.required) && Objects.equals(enumeration, that.enumeration) && Objects.equals(readOnly, that.readOnly) && Objects.equals(writeOnly, that.writeOnly) && Objects.equals(maxLength, that.maxLength) + && Objects.equals(numericRange, that.numericRange) + && Objects.equals(multipleOf, that.multipleOf) + && Objects.equals(maxItems, that.maxItems) + && Objects.equals(minItems, that.minItems) + && Objects.equals(nullable, that.nullable) + && Objects.equals(uniqueItems, that.uniqueItems) && Objects.equals(items, that.items) && Objects.equals(oneOfSchema, that.oneOfSchema) && Objects.equals(addProp, that.addProp) - && Objects.equals(extensions, that.extensions); + && Objects.equals(extensions, that.extensions) + && Objects.equals(maxProperties, that.maxProperties) + && Objects.equals(minProperties, that.minProperties); } @Override @@ -423,6 +594,8 @@ public int hashCode() { missingProperties, changeDeprecated, description, + examples, + example, changeTitle, required, changeDefault, @@ -432,11 +605,19 @@ public int hashCode() { writeOnly, changedType, maxLength, + numericRange, + multipleOf, + maxItems, + minItems, + nullable, + uniqueItems, discriminatorPropertyChanged, items, oneOfSchema, addProp, - extensions); + extensions, + maxProperties, + minProperties); } @java.lang.Override @@ -459,6 +640,10 @@ public java.lang.String toString() { + this.isChangeDeprecated() + ", description=" + this.getDescription() + + ", examples=" + + this.getExamples() + + ", example=" + + this.getExample() + ", changeTitle=" + this.isChangeTitle() + ", required=" @@ -477,6 +662,18 @@ public java.lang.String toString() { + this.isChangedType() + ", maxLength=" + this.getMaxLength() + + ", numericRange=" + + this.getNumericRange() + + ", multipleOf=" + + this.getMultipleOf() + + ", maxItems=" + + this.getMaxItems() + + ", minItems=" + + this.getMinItems() + + ", nullable=" + + this.getNullable() + + ", uniqueItems=" + + this.getUniqueItems() + ", discriminatorPropertyChanged=" + this.isDiscriminatorPropertyChanged() + ", items=" @@ -487,6 +684,10 @@ public java.lang.String toString() { + this.getAddProp() + ", extensions=" + this.getExtensions() + + ", maxProperties=" + + this.getMaxProperties() + + ", minProperties=" + + this.getMinProperties() + ")"; } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java index f8e61f7dc..0c5ed1535 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENT_SCHEMES_INCREASED; + import io.swagger.v3.oas.models.security.SecurityRequirement; import java.util.ArrayList; import java.util.List; @@ -8,14 +10,18 @@ public class ChangedSecurityRequirement implements ComposedChanged { private SecurityRequirement oldSecurityRequirement; private SecurityRequirement newSecurityRequirement; + private final DiffContext context; private SecurityRequirement missing; private SecurityRequirement increased; private List<ChangedSecurityScheme> changed; public ChangedSecurityRequirement( - SecurityRequirement oldSecurityRequirement, SecurityRequirement newSecurityRequirement) { + SecurityRequirement oldSecurityRequirement, + SecurityRequirement newSecurityRequirement, + DiffContext context) { this.oldSecurityRequirement = oldSecurityRequirement; this.newSecurityRequirement = newSecurityRequirement; + this.context = context; this.changed = new ArrayList<>(); } @@ -29,10 +35,12 @@ public DiffResult isCoreChanged() { if (increased == null && missing == null) { return DiffResult.NO_CHANGES; } - if (increased == null) { - return DiffResult.COMPATIBLE; + if (increased != null) { + if (SECURITY_REQUIREMENT_SCHEMES_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public void addMissing(String key, List<String> scopes) { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java index d8f813801..3a8f90a34 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENTS_DECREASED; + import io.swagger.v3.oas.models.security.SecurityRequirement; import java.util.ArrayList; import java.util.List; @@ -9,15 +11,18 @@ public class ChangedSecurityRequirements implements ComposedChanged { private List<SecurityRequirement> oldSecurityRequirements; private List<SecurityRequirement> newSecurityRequirements; + private final DiffContext context; private List<SecurityRequirement> missing; private List<SecurityRequirement> increased; private List<ChangedSecurityRequirement> changed; public ChangedSecurityRequirements( List<SecurityRequirement> oldSecurityRequirements, - List<SecurityRequirement> newSecurityRequirements) { + List<SecurityRequirement> newSecurityRequirements, + DiffContext context) { this.oldSecurityRequirements = oldSecurityRequirements; this.newSecurityRequirements = newSecurityRequirements; + this.context = context; this.changed = new ArrayList<>(); } @@ -31,10 +36,12 @@ public DiffResult isCoreChanged() { if (CollectionUtils.isEmpty(missing) && CollectionUtils.isEmpty(increased)) { return DiffResult.NO_CHANGES; } - if (CollectionUtils.isEmpty(missing)) { - return DiffResult.COMPATIBLE; + if (CollectionUtils.isNotEmpty(missing)) { + if (SECURITY_REQUIREMENTS_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public void addMissing(SecurityRequirement securityRequirement) { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java index f4a1682f6..1adb56b6f 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java @@ -1,5 +1,7 @@ package org.openapitools.openapidiff.core.model; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.*; + import io.swagger.v3.oas.models.security.SecurityScheme; import java.util.Arrays; import java.util.List; @@ -8,6 +10,7 @@ public class ChangedSecurityScheme implements ComposedChanged { private SecurityScheme oldSecurityScheme; private SecurityScheme newSecurityScheme; + private final DiffContext context; private boolean changedType; private boolean changedIn; private boolean changedScheme; @@ -18,9 +21,11 @@ public class ChangedSecurityScheme implements ComposedChanged { private ChangedOAuthFlows oAuthFlows; private ChangedExtensions extensions; - public ChangedSecurityScheme(SecurityScheme oldSecurityScheme, SecurityScheme newSecurityScheme) { + public ChangedSecurityScheme( + SecurityScheme oldSecurityScheme, SecurityScheme newSecurityScheme, DiffContext context) { this.oldSecurityScheme = oldSecurityScheme; this.newSecurityScheme = newSecurityScheme; + this.context = context; } @Override @@ -38,15 +43,36 @@ public DiffResult isCoreChanged() { && (changedScopes == null || changedScopes.isUnchanged())) { return DiffResult.NO_CHANGES; } - if (!changedType - && !changedIn - && !changedScheme - && !changedBearerFormat - && !changedOpenIdConnectUrl - && (changedScopes == null || changedScopes.getIncreased().isEmpty())) { - return DiffResult.COMPATIBLE; + + if (changedBearerFormat) { + if (SECURITY_SCHEME_BEARER_FORMAT_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (changedOpenIdConnectUrl) { + if (SECURITY_SCHEME_OPENIDCONNECT_URL_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (changedScheme) { + if (SECURITY_SCHEME_SCHEME_CHANGED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (changedScopes != null && !changedScopes.getIncreased().isEmpty()) { + if (SECURITY_SCHEME_SCOPES_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (changedIn || changedType) { + // TODO: Dead code removal opportunity for changedType and changedIn. It appears that + // SecuritySchemaDiff will never be given the chance to detect differences TYPE and + // IN differences because that case has already been detected and filtered out by + // SecurityRequirementsDiff and recorded as a dropped requirement in + // ChangedSecurityRequirements. + return DiffResult.INCOMPATIBLE; } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public SecurityScheme getOldSecurityScheme() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java b/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java index 7c8716ed4..f2003d375 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java @@ -1,21 +1,28 @@ package org.openapitools.openapidiff.core.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.models.PathItem; import java.util.HashMap; import java.util.Map; +import org.apache.commons.configuration2.Configuration; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; public class DiffContext { + @JsonIgnore private final OpenApiDiffOptions options; private String url; private Map<String, String> parameters; private PathItem.HttpMethod method; private boolean response; private boolean request; private Boolean required; + private String leftUrl; + private String rightUrl; - public DiffContext() { + public DiffContext(OpenApiDiffOptions options) { + this.options = options; parameters = new HashMap<>(); response = false; request = true; @@ -37,6 +44,20 @@ public DiffContext copyAsResponse() { return copy().setResponse(); } + public DiffContext copyWithLeftRightUrls(String leftUrl, String rightUrl) { + return copy().setLeftAndRightUrls(leftUrl, rightUrl); + } + + @JsonIgnore + public OpenApiDiffOptions getOptions() { + return options; + } + + @JsonIgnore + public Configuration getConfig() { + return options.getConfig(); + } + private DiffContext setRequest() { this.request = true; this.response = false; @@ -76,7 +97,7 @@ private DiffContext setMethod(PathItem.HttpMethod method) { } private DiffContext copy() { - DiffContext context = new DiffContext(); + DiffContext context = new DiffContext(options); context.url = this.url; context.parameters = this.parameters; context.method = this.method; @@ -104,6 +125,20 @@ private DiffContext setRequired(boolean required) { return this; } + public DiffContext setLeftAndRightUrls(String leftUrl, String rightUrl) { + this.leftUrl = leftUrl; + this.rightUrl = rightUrl; + return this; + } + + public String getLeftUrl() { + return this.leftUrl; + } + + public String getRightUrl() { + return this.rightUrl; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -131,6 +166,8 @@ public int hashCode() { .append(response) .append(request) .append(required) + .append(leftUrl) + .append(rightUrl) .toHashCode(); } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java index 0dcd5e6e9..ef4486628 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model.schema; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ENUM_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ENUM_INCREASED; + import java.util.List; import org.openapitools.openapidiff.core.model.ChangedList; import org.openapitools.openapidiff.core.model.DiffContext; @@ -13,10 +16,16 @@ public ChangedEnum(List<T> oldValue, List<T> newValue, DiffContext context) { @Override public DiffResult isItemsChanged() { - if (context.isRequest() && getMissing().isEmpty() - || context.isResponse() && getIncreased().isEmpty()) { - return DiffResult.COMPATIBLE; + if (context.isRequest() && !getMissing().isEmpty()) { + if (REQUEST_ENUM_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (context.isResponse() && !getIncreased().isEmpty()) { + if (RESPONSE_ENUM_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java new file mode 100644 index 000000000..1af1134b7 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java @@ -0,0 +1,43 @@ +package org.openapitools.openapidiff.core.model.schema; + +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMaxItems implements Changed { + private final Integer oldValue; + private final Integer newValue; + private final DiffContext context; + + public ChangedMaxItems(Integer oldValue, Integer newValue, DiffContext context) { + this.oldValue = oldValue; + this.newValue = newValue; + this.context = context; + } + + @Override + public DiffResult isChanged() { + if (oldValue == null && newValue == null) { + return DiffResult.NO_CHANGES; + } + if (oldValue == null || newValue == null) { + return DiffResult.COMPATIBLE; + } + if (newValue < oldValue) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; + } + + public Integer getOldValue() { + return oldValue; + } + + public Integer getNewValue() { + return newValue; + } + + public DiffContext getContext() { + return context; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java index 8bb5933da..efb068bcf 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model.schema; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_MAX_LENGTH_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_MAX_LENGTH_INCREASED; + import java.util.Objects; import org.openapitools.openapidiff.core.model.Changed; import org.openapitools.openapidiff.core.model.DiffContext; @@ -15,11 +18,17 @@ public DiffResult isChanged() { if (Objects.equals(oldValue, newValue)) { return DiffResult.NO_CHANGES; } - if (context.isRequest() && (newValue == null || oldValue != null && oldValue <= newValue) - || context.isResponse() && (oldValue == null || newValue != null && newValue <= oldValue)) { - return DiffResult.COMPATIBLE; + if (context.isRequest() && (oldValue == null || newValue != null && newValue < oldValue)) { + if (REQUEST_MAX_LENGTH_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (context.isResponse() && (newValue == null || oldValue != null && newValue > oldValue)) { + if (RESPONSE_MAX_LENGTH_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public ChangedMaxLength( diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxProperties.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxProperties.java new file mode 100644 index 000000000..c4c56af3f --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxProperties.java @@ -0,0 +1,46 @@ +package org.openapitools.openapidiff.core.model.schema; + +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMaxProperties implements Changed { + private Integer oldValue; + private Integer newValue; + private DiffContext context; + + public ChangedMaxProperties(Integer oldValue, Integer newValue, DiffContext context) { + this.oldValue = oldValue; + this.newValue = newValue; + this.context = context; + } + + public Integer getOldValue() { + return oldValue; + } + + public Integer getNewValue() { + return newValue; + } + + @Override + public DiffResult isChanged() { + if (oldValue == null && newValue == null) { + return DiffResult.NO_CHANGES; + } + + if (oldValue == null) { + return DiffResult.INCOMPATIBLE; + } + + if (newValue == null) { + return DiffResult.COMPATIBLE; + } + + if (!oldValue.equals(newValue)) { + return oldValue > newValue ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE; + } + + return DiffResult.NO_CHANGES; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java new file mode 100644 index 000000000..7791893c0 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java @@ -0,0 +1,43 @@ +package org.openapitools.openapidiff.core.model.schema; + +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMinItems implements Changed { + private final Integer oldValue; + private final Integer newValue; + private final DiffContext context; + + public ChangedMinItems(Integer oldValue, Integer newValue, DiffContext context) { + this.oldValue = oldValue; + this.newValue = newValue; + this.context = context; + } + + @Override + public DiffResult isChanged() { + if (oldValue == null && newValue == null) { + return DiffResult.NO_CHANGES; + } + if (oldValue == null || newValue == null) { + return DiffResult.COMPATIBLE; + } + if (newValue > oldValue) { + return DiffResult.INCOMPATIBLE; + } + return DiffResult.COMPATIBLE; + } + + public Integer getOldValue() { + return oldValue; + } + + public Integer getNewValue() { + return newValue; + } + + public DiffContext getContext() { + return context; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinProperties.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinProperties.java new file mode 100644 index 000000000..f574b5c80 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinProperties.java @@ -0,0 +1,46 @@ +package org.openapitools.openapidiff.core.model.schema; + +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMinProperties implements Changed { + private Integer oldValue; + private Integer newValue; + private DiffContext context; + + public ChangedMinProperties(Integer oldValue, Integer newValue, DiffContext context) { + this.oldValue = oldValue; + this.newValue = newValue; + this.context = context; + } + + public Integer getOldValue() { + return oldValue; + } + + public Integer getNewValue() { + return newValue; + } + + @Override + public DiffResult isChanged() { + if (oldValue == null && newValue == null) { + return DiffResult.NO_CHANGES; + } + + if (oldValue == null) { + return DiffResult.INCOMPATIBLE; + } + + if (newValue == null) { + return DiffResult.COMPATIBLE; + } + + if (!oldValue.equals(newValue)) { + return newValue > oldValue ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE; + } + + return DiffResult.NO_CHANGES; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMultipleOf.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMultipleOf.java new file mode 100644 index 000000000..adaf15710 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMultipleOf.java @@ -0,0 +1,57 @@ +package org.openapitools.openapidiff.core.model.schema; + +import java.math.BigDecimal; +import java.util.Objects; +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedMultipleOf implements Changed { + + private final BigDecimal left; + private final BigDecimal right; + + public ChangedMultipleOf(BigDecimal leftMultipleOf, BigDecimal rightMultipleOf) { + this.left = leftMultipleOf; + this.right = rightMultipleOf; + } + + @Override + public DiffResult isChanged() { + if (Objects.equals(left, right)) { + return DiffResult.NO_CHANGES; + } + + // multipleof removed -> compatible + if (right == null) { + return DiffResult.COMPATIBLE; + } + + return DiffResult.INCOMPATIBLE; + } + + public BigDecimal getLeft() { + return left; + } + + public BigDecimal getRight() { + return right; + } + + @Override + public String toString() { + return "ChangedMultipleOf [left=" + left + ", right=" + right + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChangedMultipleOf that = (ChangedMultipleOf) o; + return Objects.equals(left, that.getLeft()) && Objects.equals(right, that.getRight()); + } + + @Override + public int hashCode() { + return Objects.hash(left, right); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNullable.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNullable.java new file mode 100644 index 000000000..befc1d16d --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNullable.java @@ -0,0 +1,54 @@ +package org.openapitools.openapidiff.core.model.schema; + +import java.util.Objects; +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedNullable implements Changed { + + private final Boolean left; + private final Boolean right; + + public ChangedNullable(Boolean leftNullable, Boolean rightNullable) { + this.left = leftNullable; + this.right = rightNullable; + } + + @Override + public DiffResult isChanged() { + boolean leftValue = left != null && left; + boolean rightValue = right != null && right; + + if (leftValue != rightValue) { + return DiffResult.COMPATIBLE; + } + + return DiffResult.NO_CHANGES; + } + + public Boolean getLeft() { + return left; + } + + public Boolean getRight() { + return right; + } + + @Override + public String toString() { + return "ChangedNullable [left=" + left + ", right=" + right + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChangedNullable that = (ChangedNullable) o; + return Objects.equals(left, that.getLeft()) && Objects.equals(right, that.getRight()); + } + + @Override + public int hashCode() { + return Objects.hash(left, right); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNumericRange.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNumericRange.java new file mode 100644 index 000000000..151c12fb6 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNumericRange.java @@ -0,0 +1,182 @@ +package org.openapitools.openapidiff.core.model.schema; + +import static org.apache.commons.lang3.BooleanUtils.isTrue; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_NUMERIC_RANGE_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_NUMERIC_RANGE_INCREASED; + +import java.math.BigDecimal; +import java.util.Objects; +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; + +public final class ChangedNumericRange implements Changed { + private final BigDecimal oldMinimumValue; + private final BigDecimal newMinimumValue; + private final BigDecimal oldMaximumValue; + private final BigDecimal newMaximumValue; + private final Boolean oldMinimumExclusiveValue; + private final Boolean newMinimumExclusiveValue; + private final Boolean oldMaximumExclusiveValue; + private final Boolean newMaximumExclusiveValue; + private final DiffContext context; + + @Override + public DiffResult isChanged() { + if (Objects.equals(oldMinimumValue, newMinimumValue) + && Objects.equals(oldMaximumValue, newMaximumValue) + && Objects.equals(oldMinimumExclusiveValue, newMinimumExclusiveValue) + && Objects.equals(oldMaximumExclusiveValue, newMaximumExclusiveValue)) { + return DiffResult.NO_CHANGES; + } + + if ((context.isRequest() && !REQUEST_NUMERIC_RANGE_DECREASED.enabled(context)) + || (context.isResponse() && !RESPONSE_NUMERIC_RANGE_INCREASED.enabled(context))) { + return DiffResult.COMPATIBLE; + } + + boolean exclusiveMaxOld = isTrue(oldMaximumExclusiveValue); + boolean exclusiveMinOld = isTrue(oldMinimumExclusiveValue); + boolean exclusiveMaxNew = isTrue(newMaximumExclusiveValue); + boolean exclusiveMinNew = isTrue(newMinimumExclusiveValue); + int diffMax = compare(oldMaximumValue, newMaximumValue, false); + int diffMin = compare(oldMinimumValue, newMinimumValue, true); + + if (context.isRequest()) { + if (diffMax > 0 || (diffMax == 0 && !exclusiveMaxOld && exclusiveMaxNew)) { + return DiffResult.INCOMPATIBLE; + } + if (diffMin < 0 || (diffMin == 0 && !exclusiveMinOld && exclusiveMinNew)) { + return DiffResult.INCOMPATIBLE; + } + } else if (context.isResponse()) { + if (diffMax < 0 || (diffMax == 0 && exclusiveMaxOld && !exclusiveMaxNew)) { + return DiffResult.INCOMPATIBLE; + } + if (diffMin > 0 || (diffMin == 0 && exclusiveMinOld && !exclusiveMinNew)) { + return DiffResult.INCOMPATIBLE; + } + } + return DiffResult.COMPATIBLE; + } + + private int compare(BigDecimal left, BigDecimal right, boolean nullMeansLessThan) { + if (left == null && right == null) { + return 0; + } + if (left == null) { + return nullMeansLessThan ? -1 : 1; + } + if (right == null) { + return nullMeansLessThan ? 1 : -1; + } + return left.unscaledValue().compareTo(right.unscaledValue()); + } + + public ChangedNumericRange( + final BigDecimal oldMinimumValue, + final BigDecimal newMinimumValue, + final BigDecimal oldMaximumValue, + final BigDecimal newMaximumValue, + final Boolean oldMinimumExclusiveValue, + final Boolean newMinimumExclusiveValue, + final Boolean oldMaximumExclusiveValue, + final Boolean newMaximumExclusiveValue, + final DiffContext context) { + this.oldMinimumValue = oldMinimumValue; + this.newMinimumValue = newMinimumValue; + this.oldMaximumValue = oldMaximumValue; + this.newMaximumValue = newMaximumValue; + this.oldMinimumExclusiveValue = oldMinimumExclusiveValue; + this.newMinimumExclusiveValue = newMinimumExclusiveValue; + this.oldMaximumExclusiveValue = oldMaximumExclusiveValue; + this.newMaximumExclusiveValue = newMaximumExclusiveValue; + this.context = context; + } + + public BigDecimal getOldMinimumValue() { + return oldMinimumValue; + } + + public BigDecimal getNewMinimumValue() { + return newMinimumValue; + } + + public BigDecimal getOldMaximumValue() { + return oldMaximumValue; + } + + public BigDecimal getNewMaximumValue() { + return newMaximumValue; + } + + public Boolean getOldMinimumExclusiveValue() { + return oldMinimumExclusiveValue; + } + + public Boolean getNewMinimumExclusiveValue() { + return newMinimumExclusiveValue; + } + + public Boolean getOldMaximumExclusiveValue() { + return oldMaximumExclusiveValue; + } + + public Boolean getNewMaximumExclusiveValue() { + return newMaximumExclusiveValue; + } + + public DiffContext getContext() { + return this.context; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChangedNumericRange that = (ChangedNumericRange) o; + return Objects.equals(oldMinimumValue, newMinimumValue) + && Objects.equals(oldMaximumValue, newMaximumValue) + && Objects.equals(oldMinimumExclusiveValue, newMinimumExclusiveValue) + && Objects.equals(oldMaximumExclusiveValue, newMaximumExclusiveValue) + && Objects.equals(context, that.context); + } + + @Override + public int hashCode() { + return Objects.hash( + oldMinimumValue, + newMinimumValue, + oldMaximumValue, + newMaximumValue, + oldMinimumExclusiveValue, + newMinimumExclusiveValue, + oldMaximumExclusiveValue, + newMaximumExclusiveValue, + context); + } + + @Override + public String toString() { + return "ChangedNumericRange(" + + "oldMinimumValue=" + + oldMinimumValue + + ", newMinimumValue=" + + newMinimumValue + + ", oldMaximumValue=" + + oldMaximumValue + + ", newMaximumValue=" + + newMaximumValue + + ", oldMinimumExclusiveValue=" + + oldMinimumExclusiveValue + + ", newMinimumExclusiveValue=" + + newMinimumExclusiveValue + + ", oldMaximumExclusiveValue=" + + oldMaximumExclusiveValue + + ", newMaximumExclusiveValue=" + + newMaximumExclusiveValue + + ", context=" + + context + + ')'; + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOneOfSchema.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedOneOfSchema.java similarity index 75% rename from core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOneOfSchema.java rename to core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedOneOfSchema.java index ebf07d2b7..60159f569 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOneOfSchema.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedOneOfSchema.java @@ -1,10 +1,18 @@ -package org.openapitools.openapidiff.core.model; +package org.openapitools.openapidiff.core.model.schema; + +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ONEOF_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ONEOF_INCREASED; import io.swagger.v3.oas.models.media.Schema; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.ChangedSchema; +import org.openapitools.openapidiff.core.model.ComposedChanged; +import org.openapitools.openapidiff.core.model.DiffContext; +import org.openapitools.openapidiff.core.model.DiffResult; public class ChangedOneOfSchema implements ComposedChanged { private final Map<String, String> oldMapping; @@ -31,10 +39,17 @@ public DiffResult isCoreChanged() { if (increased.isEmpty() && missing.isEmpty()) { return DiffResult.NO_CHANGES; } - if (context.isRequest() && missing.isEmpty() || context.isResponse() && increased.isEmpty()) { - return DiffResult.COMPATIBLE; + if (context.isRequest() && !missing.isEmpty()) { + if (REQUEST_ONEOF_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (context.isResponse() && !increased.isEmpty()) { + if (RESPONSE_ONEOF_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } public Map<String, String> getOldMapping() { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java index 25d86af8f..9bde49acd 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model.schema; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_REQUIRED_DECREASED; + import java.util.Objects; import java.util.Optional; import org.openapitools.openapidiff.core.model.Changed; @@ -29,10 +32,16 @@ public DiffResult isChanged() { } if (context.isRequest()) { if (Boolean.TRUE.equals(newValue)) { - return DiffResult.INCOMPATIBLE; - } else { - return context.isRequired() ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE; + if (REQUEST_READONLY_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } else if (context.isRequired()) { + // Incompatible because a prev RO prop (invalid) is now not RO and required + if (REQUEST_READONLY_REQUIRED_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } + return DiffResult.COMPATIBLE; } return DiffResult.UNKNOWN; } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java index d6b41f3cd..58a9ae49a 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model.schema; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_REQUIRED_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED; + import java.util.List; import org.openapitools.openapidiff.core.model.ChangedList; import org.openapitools.openapidiff.core.model.DiffContext; @@ -13,10 +16,16 @@ public ChangedRequired(List<String> oldValue, List<String> newValue, DiffContext @Override public DiffResult isItemsChanged() { - if (context.isRequest() && getIncreased().isEmpty() - || context.isResponse() && getMissing().isEmpty()) { - return DiffResult.COMPATIBLE; + if (context.isRequest() && !getIncreased().isEmpty()) { + if (REQUEST_REQUIRED_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } + if (context.isResponse() && !getMissing().isEmpty()) { + if (RESPONSE_REQUIRED_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } - return DiffResult.INCOMPATIBLE; + return DiffResult.COMPATIBLE; } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedUniqueItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedUniqueItems.java new file mode 100644 index 000000000..ecccdf674 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedUniqueItems.java @@ -0,0 +1,58 @@ +package org.openapitools.openapidiff.core.model.schema; + +import java.util.Objects; +import org.openapitools.openapidiff.core.model.Changed; +import org.openapitools.openapidiff.core.model.DiffResult; + +public class ChangedUniqueItems implements Changed { + + private final Boolean left; + private final Boolean right; + + public ChangedUniqueItems(Boolean leftNullable, Boolean rightNullable) { + this.left = leftNullable; + this.right = rightNullable; + } + + @Override + public DiffResult isChanged() { + boolean leftValue = left != null && left; + boolean rightValue = right != null && right; + + if (leftValue == false && rightValue == true) { + return DiffResult.INCOMPATIBLE; + } + + if (leftValue == true && rightValue == false) { + return DiffResult.COMPATIBLE; + } + + return DiffResult.NO_CHANGES; + } + + public Boolean getLeft() { + return left; + } + + public Boolean getRight() { + return right; + } + + @Override + public String toString() { + return "ChangedUniqueItems [left=" + left + ", right=" + right + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChangedNullable that = (ChangedNullable) o; + return Objects.equals(left, that.getLeft()) && Objects.equals(right, that.getRight()); + } + + @Override + public int hashCode() { + return Objects.hash(left, right); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java index 91d19a644..110b6ede2 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java @@ -1,5 +1,8 @@ package org.openapitools.openapidiff.core.model.schema; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_REQUIRED_DECREASED; + import java.util.Objects; import java.util.Optional; import org.openapitools.openapidiff.core.model.Changed; @@ -29,10 +32,15 @@ public DiffResult isChanged() { } if (context.isResponse()) { if (Boolean.TRUE.equals(newValue)) { - return DiffResult.INCOMPATIBLE; - } else { - return context.isRequired() ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE; + if (RESPONSE_WRITEONLY_INCREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } + } else if (context.isRequired()) { + if (RESPONSE_WRITEONLY_REQUIRED_DECREASED.enabled(context)) { + return DiffResult.INCOMPATIBLE; + } } + return DiffResult.COMPATIBLE; } return DiffResult.UNKNOWN; } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/AsciidocRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/AsciidocRender.java new file mode 100644 index 000000000..997241dc2 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/AsciidocRender.java @@ -0,0 +1,311 @@ +package org.openapitools.openapidiff.core.output; + +import static org.openapitools.openapidiff.core.model.Changed.result; + +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.responses.ApiResponse; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.openapitools.openapidiff.core.exception.RendererException; +import org.openapitools.openapidiff.core.model.*; +import org.openapitools.openapidiff.core.utils.RefPointer; +import org.openapitools.openapidiff.core.utils.RefType; + +public class AsciidocRender implements Render { + private static final int LINE_LENGTH = 74; + protected static RefPointer<Schema<?>> refPointer = new RefPointer<>(RefType.SCHEMAS); + protected ChangedOpenApi diff; + + @Override + public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) { + this.diff = diff; + if (diff.isUnchanged()) { + safelyAppend( + outputStreamWriter, + bigTitle( + diff.getNewSpecOpenApi().getInfo().getTitle(), + diff.getNewSpecOpenApi().getInfo().getVersion())); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, "NOTE: No differences. Specifications are equivalents"); + } else { + safelyAppend( + outputStreamWriter, + bigTitle( + diff.getNewSpecOpenApi().getInfo().getTitle(), + diff.getNewSpecOpenApi().getInfo().getVersion())); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, ":reproducible:\n:sectlinks:\n:toc:\n"); + safelyAppend(outputStreamWriter, System.lineSeparator()); + + List<Endpoint> newEndpoints = diff.getNewEndpoints(); + listEndpoints(newEndpoints, "What's New", outputStreamWriter); + + List<Endpoint> missingEndpoints = diff.getMissingEndpoints(); + listEndpoints(missingEndpoints, "What's Deleted", outputStreamWriter); + + List<Endpoint> deprecatedEndpoints = diff.getDeprecatedEndpoints(); + listEndpoints(deprecatedEndpoints, "What's Deprecated", outputStreamWriter); + + List<ChangedOperation> changedOperations = diff.getChangedOperations(); + ol_changed(changedOperations, outputStreamWriter); + + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend( + outputStreamWriter, + diff.isCompatible() + ? "NOTE: API changes are backward compatible" + : "WARNING: API changes broke backward compatibility"); + safelyAppend(outputStreamWriter, System.lineSeparator()); + } + try { + outputStreamWriter.close(); + } catch (IOException e) { + throw new RendererException(e); + } + } + + private void ol_changed( + List<ChangedOperation> operations, OutputStreamWriter outputStreamWriter) { + if (null == operations || operations.isEmpty()) { + return; + } + safelyAppend(outputStreamWriter, title("What's Changed", 2)); + safelyAppend(outputStreamWriter, System.lineSeparator()); + for (ChangedOperation operation : operations) { + String pathUrl = operation.getPathUrl(); + String method = operation.getHttpMethod().toString(); + String desc = + Optional.ofNullable(operation.getSummary()).map(ChangedMetadata::getRight).orElse(""); + + safelyAppend(outputStreamWriter, itemEndpoint(method, pathUrl, desc)); + if (result(operation.getParameters()).isDifferent()) { + safelyAppend(outputStreamWriter, "* Parameter:\n"); + safelyAppend(outputStreamWriter, ul_param(operation.getParameters())); + safelyAppend(outputStreamWriter, System.lineSeparator()); + } + if (operation.resultRequestBody().isDifferent()) { + safelyAppend(outputStreamWriter, "* Request:\n"); + safelyAppend( + outputStreamWriter, ul_content(operation.getRequestBody().getContent(), true, 2)); + safelyAppend(outputStreamWriter, System.lineSeparator()); + } + if (operation.resultApiResponses().isDifferent()) { + safelyAppend(outputStreamWriter, "* Return Type:\n"); + safelyAppend(outputStreamWriter, ul_response(operation.getApiResponses())); + safelyAppend(outputStreamWriter, System.lineSeparator()); + } + } + } + + private String ul_response(ChangedApiResponse changedApiResponse) { + Map<String, ApiResponse> addResponses = changedApiResponse.getIncreased(); + Map<String, ApiResponse> delResponses = changedApiResponse.getMissing(); + Map<String, ChangedResponse> changedResponses = changedApiResponse.getChanged(); + StringBuilder sb = new StringBuilder(); + for (String propName : addResponses.keySet()) { + sb.append(itemResponse("** Add ", propName)); + } + for (String propName : delResponses.keySet()) { + sb.append(itemResponse("** Deleted ", propName)); + } + for (Entry<String, ChangedResponse> entry : changedResponses.entrySet()) { + sb.append(itemChangedResponse("** Changed ", entry.getKey(), entry.getValue())); + } + return sb.toString(); + } + + private String itemResponse(String title, String code) { + StringBuilder sb = new StringBuilder(); + String status = ""; + if (!code.equals("default") && !code.matches("[1-5]XX")) { + status = HttpStatus.getReasonPhrase(Integer.parseInt(code)); + } + sb.append(title).append(code).append(' ').append(status).append("\n"); + return sb.toString(); + } + + private String itemChangedResponse(String title, String contentType, ChangedResponse response) { + return itemResponse(title, contentType) + + "** Media types:\n" + + ul_content(response.getContent(), false, 3); + } + + private String ul_content(ChangedContent changedContent, boolean isRequest, int indent) { + StringBuilder sb = new StringBuilder(); + if (changedContent == null) { + return sb.toString(); + } + for (String propName : changedContent.getIncreased().keySet()) { + sb.append(itemContent("Added ", propName, indent)); + } + for (String propName : changedContent.getMissing().keySet()) { + sb.append(itemContent("Deleted ", propName, indent)); + } + for (String propName : changedContent.getChanged().keySet()) { + sb.append( + itemContent( + "Changed ", propName, indent, changedContent.getChanged().get(propName), isRequest)); + } + return sb.toString(); + } + + private String itemContent(String title, String contentType, int indent) { + return StringUtils.repeat('*', indent) + " " + title + contentType + "\n"; + } + + private String itemContent( + String title, + String contentType, + int indent, + ChangedMediaType changedMediaType, + boolean isRequest) { + StringBuilder sb = new StringBuilder(); + sb.append(itemContent(title, contentType, indent)) + .append(itemContent("Schema:", "", indent)) + .append(changedMediaType.isCompatible() ? "Backward compatible" : "Broken compatibility") + .append("\n"); + if (!changedMediaType.isCompatible()) { + sb.append(incompatibilities(changedMediaType.getSchema())); + } + return sb.toString(); + } + + private String incompatibilities(final ChangedSchema schema) { + return incompatibilities("", schema); + } + + private String incompatibilities(String propName, final ChangedSchema schema) { + StringBuilder sb = new StringBuilder(); + if (schema.getItems() != null) { + sb.append(items(propName, schema.getItems())); + } + if (schema.isCoreChanged() == DiffResult.INCOMPATIBLE && schema.isChangedType()) { + String type = type(schema.getOldSchema()) + " -> " + type(schema.getNewSchema()); + sb.append(property(propName, "Changed property type", type)); + } + String prefix = propName.isEmpty() ? "" : propName + "."; + sb.append( + properties(prefix, "Missing property", schema.getMissingProperties(), schema.getContext())); + schema + .getChangedProperties() + .forEach((name, property) -> sb.append(incompatibilities(prefix + name, property))); + return sb.toString(); + } + + private String items(String propName, ChangedSchema schema) { + return incompatibilities(propName + "[n]", schema); + } + + private String properties( + String propPrefix, String title, Map<String, Schema<?>> properties, DiffContext context) { + StringBuilder sb = new StringBuilder(); + if (properties != null) { + properties.forEach((key, value) -> sb.append(resolveProperty(propPrefix, value, key, title))); + } + return sb.toString(); + } + + private String resolveProperty(String propPrefix, Schema<?> value, String key, String title) { + try { + return property(propPrefix + key, title, resolve(value)); + } catch (Exception e) { + return property(propPrefix + key, title, type(value)); + } + } + + protected String property(String name, String title, Schema<?> schema) { + return property(name, title, type(schema)); + } + + protected String property(String name, String title, String type) { + return String.format("*** %s: %s (%s)%n\n", title, name, type); + } + + protected Schema<?> resolve(Schema<?> schema) { + return refPointer.resolveRef( + diff.getNewSpecOpenApi().getComponents(), schema, schema.get$ref()); + } + + protected String type(Schema<?> schema) { + String result = "object"; + if (schema == null) { + result = "no schema"; + } else if (schema instanceof ArraySchema) { + result = "array"; + } else if (schema.getType() != null) { + result = schema.getType(); + } + return result; + } + + private String ul_param(ChangedParameters changedParameters) { + List<Parameter> addParameters = changedParameters.getIncreased(); + List<Parameter> delParameters = changedParameters.getMissing(); + List<ChangedParameter> changed = changedParameters.getChanged(); + StringBuilder sb = new StringBuilder(); + for (Parameter param : addParameters) { + sb.append(itemParam("** Add ", param)); + } + for (ChangedParameter param : changed) { + sb.append(li_changedParam(param)); + } + for (Parameter param : delParameters) { + sb.append(itemParam("** Delete ", param)); + } + return sb.toString(); + } + + private String itemParam(String title, Parameter param) { + return title + param.getName() + " in " + param.getIn() + System.lineSeparator(); + } + + private String li_changedParam(ChangedParameter changeParam) { + if (changeParam.isDeprecated()) { + return itemParam("** Deprecated ", changeParam.getNewParameter()); + } else { + return itemParam("** Changed ", changeParam.getNewParameter()); + } + } + + private String listEndpoints( + List<Endpoint> endpoints, String title, OutputStreamWriter outputStreamWriter) { + if (null == endpoints || endpoints.isEmpty()) { + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append(title(title)); + for (Endpoint endpoint : endpoints) { + sb.append( + itemEndpoint( + endpoint.getMethod().toString(), endpoint.getPathUrl(), endpoint.getSummary())); + } + return sb.append(System.lineSeparator()).toString(); + } + + private String itemEndpoint(String method, String path, String desc) { + return String.format("=== %s%s%n", StringUtils.rightPad(method, 6), path); + } + + public String bigTitle(String title, String version) { + char ch = '='; + + return String.format("= %s (v %s)", title.toUpperCase(), version); + } + + public String title(String title) { + return this.title(title, '-'); + } + + public String title(String title, int level) { + String little = StringUtils.repeat("=", level); + return String.format("%s %s", little, title); + } +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java index 7ca4acc44..4f8e10fc9 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java @@ -6,12 +6,14 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; +import java.io.IOException; +import java.io.OutputStreamWriter; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.lang3.StringUtils; +import org.openapitools.openapidiff.core.exception.RendererException; import org.openapitools.openapidiff.core.model.*; import org.openapitools.openapidiff.core.utils.RefPointer; import org.openapitools.openapidiff.core.utils.RefType; @@ -22,81 +24,80 @@ public class ConsoleRender implements Render { protected ChangedOpenApi diff; @Override - public String render(ChangedOpenApi diff) { + public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) { this.diff = diff; - StringBuilder output = new StringBuilder(); if (diff.isUnchanged()) { - output.append("No differences. Specifications are equivalents"); + safelyAppend(outputStreamWriter, "No differences. Specifications are equivalents"); } else { - output - .append(bigTitle("Api Change Log")) - .append(StringUtils.center(diff.getNewSpecOpenApi().getInfo().getTitle(), LINE_LENGTH)) - .append(System.lineSeparator()); + safelyAppend(outputStreamWriter, bigTitle("Api Change Log")); + safelyAppend( + outputStreamWriter, + StringUtils.center(diff.getNewSpecOpenApi().getInfo().getTitle(), LINE_LENGTH)); + safelyAppend(outputStreamWriter, System.lineSeparator()); List<Endpoint> newEndpoints = diff.getNewEndpoints(); - String ol_newEndpoint = listEndpoints(newEndpoints, "What's New"); + listEndpoints(newEndpoints, "What's New", outputStreamWriter); List<Endpoint> missingEndpoints = diff.getMissingEndpoints(); - String ol_missingEndpoint = listEndpoints(missingEndpoints, "What's Deleted"); + listEndpoints(missingEndpoints, "What's Deleted", outputStreamWriter); List<Endpoint> deprecatedEndpoints = diff.getDeprecatedEndpoints(); - String ol_deprecatedEndpoint = listEndpoints(deprecatedEndpoints, "What's Deprecated"); + listEndpoints(deprecatedEndpoints, "What's Deprecated", outputStreamWriter); List<ChangedOperation> changedOperations = diff.getChangedOperations(); - String ol_changed = ol_changed(changedOperations); - - output - .append(renderBody(ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed)) - .append(title("Result")) - .append( - StringUtils.center( - diff.isCompatible() - ? "API changes are backward compatible" - : "API changes broke backward compatibility", - LINE_LENGTH)) - .append(System.lineSeparator()) - .append(separator('-')); + ol_changed(changedOperations, outputStreamWriter); + + safelyAppend(outputStreamWriter, title("Result")); + safelyAppend( + outputStreamWriter, + StringUtils.center( + diff.isCompatible() + ? "API changes are backward compatible" + : "API changes broke backward compatibility", + LINE_LENGTH)); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, separator('-')); + } + try { + outputStreamWriter.close(); + } catch (IOException e) { + throw new RendererException(e); } - return output.toString(); } - private String ol_changed(List<ChangedOperation> operations) { + private void ol_changed( + List<ChangedOperation> operations, OutputStreamWriter outputStreamWriter) { if (null == operations || operations.isEmpty()) { - return ""; + return; } - StringBuilder sb = new StringBuilder(); - sb.append(title("What's Changed")); + safelyAppend(outputStreamWriter, title("What's Changed")); for (ChangedOperation operation : operations) { String pathUrl = operation.getPathUrl(); String method = operation.getHttpMethod().toString(); String desc = Optional.ofNullable(operation.getSummary()).map(ChangedMetadata::getRight).orElse(""); - StringBuilder ul_detail = new StringBuilder(); + safelyAppend(outputStreamWriter, itemEndpoint(method, pathUrl, desc)); + if (result(operation.getParameters()).isDifferent()) { - ul_detail - .append(StringUtils.repeat(' ', 2)) - .append("Parameter:") - .append(System.lineSeparator()) - .append(ul_param(operation.getParameters())); + safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2)); + safelyAppend(outputStreamWriter, "Parameter:"); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, ul_param(operation.getParameters())); } if (operation.resultRequestBody().isDifferent()) { - ul_detail - .append(StringUtils.repeat(' ', 2)) - .append("Request:") - .append(System.lineSeparator()) - .append(ul_content(operation.getRequestBody().getContent(), true)); + safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2)); + safelyAppend(outputStreamWriter, "Request:"); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, ul_content(operation.getRequestBody().getContent(), true)); } if (operation.resultApiResponses().isDifferent()) { - ul_detail - .append(StringUtils.repeat(' ', 2)) - .append("Return Type:") - .append(System.lineSeparator()) - .append(ul_response(operation.getApiResponses())); + safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2)); + safelyAppend(outputStreamWriter, "Return Type:"); + safelyAppend(outputStreamWriter, System.lineSeparator()); + safelyAppend(outputStreamWriter, ul_response(operation.getApiResponses())); } - sb.append(itemEndpoint(method, pathUrl, desc)).append(ul_detail); } - return sb.toString(); } private String ul_response(ChangedApiResponse changedApiResponse) { @@ -119,8 +120,8 @@ private String ul_response(ChangedApiResponse changedApiResponse) { private String itemResponse(String title, String code) { StringBuilder sb = new StringBuilder(); String status = ""; - if (!code.equals("default")) { - status = HttpStatus.getStatusText(Integer.parseInt(code)); + if (!code.equals("default") && !code.matches("[1-5]XX")) { + status = HttpStatus.getReasonPhrase(Integer.parseInt(code)); } sb.append(StringUtils.repeat(' ', 4)) .append("- ") @@ -280,9 +281,10 @@ private String li_changedParam(ChangedParameter changeParam) { } } - private String listEndpoints(List<Endpoint> endpoints, String title) { + private void listEndpoints( + List<Endpoint> endpoints, String title, OutputStreamWriter outputStreamWriter) { if (null == endpoints || endpoints.isEmpty()) { - return ""; + return; } StringBuilder sb = new StringBuilder(); sb.append(title(title)); @@ -291,7 +293,8 @@ private String listEndpoints(List<Endpoint> endpoints, String title) { itemEndpoint( endpoint.getMethod().toString(), endpoint.getPathUrl(), endpoint.getSummary())); } - return sb.append(System.lineSeparator()).toString(); + + safelyAppend(outputStreamWriter, sb.append(System.lineSeparator()).toString()); } private String itemEndpoint(String method, String path, String desc) { @@ -318,8 +321,7 @@ public String title(String title, char ch) { separator(ch), little, StringUtils.center(title, LINE_LENGTH - 4), little, separator(ch)); } - public StringBuilder separator(char ch) { - StringBuilder sb = new StringBuilder(); - return sb.append(StringUtils.repeat(ch, LINE_LENGTH)).append(System.lineSeparator()); + public String separator(char ch) { + return StringUtils.repeat(ch, LINE_LENGTH) + System.lineSeparator(); } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java index 0513424cd..eb4c5b884 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java @@ -26,16 +26,21 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import j2html.rendering.FlatHtml; import j2html.tags.ContainerTag; import j2html.tags.specialized.DivTag; import j2html.tags.specialized.HtmlTag; import j2html.tags.specialized.LiTag; import j2html.tags.specialized.OlTag; import j2html.tags.specialized.UlTag; +import java.io.IOException; +import java.io.OutputStreamWriter; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import org.openapitools.openapidiff.core.exception.RendererException; import org.openapitools.openapidiff.core.model.ChangedApiResponse; import org.openapitools.openapidiff.core.model.ChangedContent; import org.openapitools.openapidiff.core.model.ChangedMediaType; @@ -46,6 +51,8 @@ import org.openapitools.openapidiff.core.model.ChangedParameters; import org.openapitools.openapidiff.core.model.ChangedResponse; import org.openapitools.openapidiff.core.model.ChangedSchema; +import org.openapitools.openapidiff.core.model.ChangedSecurityRequirement; +import org.openapitools.openapidiff.core.model.ChangedSecurityRequirements; import org.openapitools.openapidiff.core.model.DiffContext; import org.openapitools.openapidiff.core.model.DiffResult; import org.openapitools.openapidiff.core.model.Endpoint; @@ -60,18 +67,30 @@ public class HtmlRender implements Render { private final String title; private final String linkCss; + private final boolean showAllChanges; protected ChangedOpenApi diff; public HtmlRender() { this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); } + public HtmlRender(boolean showAllChanges) { + this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css", showAllChanges); + } + public HtmlRender(String title, String linkCss) { this.title = title; this.linkCss = linkCss; + this.showAllChanges = false; } - public String render(ChangedOpenApi diff) { + public HtmlRender(String title, String linkCss, boolean showAllChanges) { + this.title = title; + this.linkCss = linkCss; + this.showAllChanges = showAllChanges; + } + + public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) { this.diff = diff; List<Endpoint> newEndpoints = diff.getNewEndpoints(); @@ -86,10 +105,16 @@ public String render(ChangedOpenApi diff) { List<ChangedOperation> changedOperations = diff.getChangedOperations(); OlTag ol_changed = ol_changed(changedOperations); - return renderHtml(ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed); + renderHtml( + ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed, outputStreamWriter); } - public String renderHtml(OlTag ol_new, OlTag ol_miss, OlTag ol_deprec, OlTag ol_changed) { + public void renderHtml( + OlTag ol_new, + OlTag ol_miss, + OlTag ol_deprec, + OlTag ol_changed, + OutputStreamWriter outputStreamWriter) { HtmlTag html = html() .attr("lang", "en") @@ -110,7 +135,14 @@ public String renderHtml(OlTag ol_new, OlTag ol_miss, OlTag ol_deprec, OlTag ol_ div().with(h2("What's Deprecated"), hr(), ol_deprec), div().with(h2("What's Changed"), hr(), ol_changed)))); - return document().render() + html.render(); + try { + FlatHtml<OutputStreamWriter> flatHtml = FlatHtml.into(outputStreamWriter); + document().render(flatHtml); + html.render(flatHtml); + outputStreamWriter.close(); + } catch (IOException e) { + throw new RendererException("Problem rendering html document.", e); + } } private OlTag ol_newEndpoint(List<Endpoint> endpoints) { @@ -183,6 +215,11 @@ private OlTag ol_changed(List<ChangedOperation> changedOperations) { ul_detail.with( li().with(h3("Response")).with(ul_response(changedOperation.getApiResponses()))); } + if (showAllChanges && changedOperation.resultSecurityRequirements().isDifferent()) { + ul_detail.with( + li().with(h3("Security Requirements")) + .with(ul_securityRequirements(changedOperation.getSecurityRequirements()))); + } ol.with( li().with(span(method).withClass(method)) .withText(pathUrl + " ") @@ -192,6 +229,52 @@ private OlTag ol_changed(List<ChangedOperation> changedOperations) { return ol; } + private UlTag ul_securityRequirements(ChangedSecurityRequirements changedSecurityRequirements) { + List<SecurityRequirement> addRequirements = changedSecurityRequirements.getIncreased(); + List<SecurityRequirement> delRequirements = changedSecurityRequirements.getMissing(); + List<ChangedSecurityRequirement> changedRequirements = changedSecurityRequirements.getChanged(); + UlTag ul = ul().withClass("change security requirements"); + if (addRequirements != null) { + for (SecurityRequirement addRequirement : addRequirements) { + ul.with(li_addSecurityRequirement(addRequirement)); + } + } + if (delRequirements != null) { + for (SecurityRequirement delRequirement : delRequirements) { + ul.with(li_missingSecurityRequirement(delRequirement)); + } + } + if (changedRequirements != null) { + for (ChangedSecurityRequirement changedRequirement : changedRequirements) { + ul.with(li_changedSecurityRequirement(changedRequirement)); + } + } + + return ul; + } + + private LiTag li_addSecurityRequirement(SecurityRequirement securityRequirement) { + return li().withText("New security requirement : ") + .with(span(null == securityRequirement.toString() ? "" : (securityRequirement.toString()))); + } + + private LiTag li_missingSecurityRequirement(SecurityRequirement securityRequirement) { + return li().withText("Deleted security requirement : ") + .with(span(null == securityRequirement.toString() ? "" : (securityRequirement.toString()))); + } + + private LiTag li_changedSecurityRequirement( + ChangedSecurityRequirement changedSecurityRequirement) { + return li().withText(String.format("Changed security requirement : ")) + .with( + span( + (null == changedSecurityRequirement.getNewSecurityRequirement() + || null + == changedSecurityRequirement.getNewSecurityRequirement().toString()) + ? "" + : (changedSecurityRequirement.getNewSecurityRequirement().toString()))); + } + private UlTag ul_response(ChangedApiResponse changedApiResponse) { Map<String, ApiResponse> addResponses = changedApiResponse.getIncreased(); Map<String, ApiResponse> delResponses = changedApiResponse.getMissing(); @@ -262,8 +345,10 @@ private LiTag li_changedRequest(String name, ChangedMediaType request) { LiTag li = li().with(div_changedSchema(request.getSchema())) .withText(String.format("Changed body: '%s'", name)); - if (request.isIncompatible()) { + if (request.isIncompatible() && !showAllChanges) { incompatibilities(li, request.getSchema()); + } else if (showAllChanges) { + allChanges(li, request.getSchema()); } return li; } @@ -274,6 +359,28 @@ private DivTag div_changedSchema(ChangedSchema schema) { return div; } + private void allChanges(final LiTag output, final ChangedSchema schema) { + allChanges(output, "", schema); + } + + private void allChanges( + final ContainerTag<?> output, String propName, final ChangedSchema schema) { + String prefix = propName.isEmpty() ? "" : propName + "."; + properties( + output, prefix, "Missing property", schema.getMissingProperties(), schema.getContext()); + properties( + output, prefix, "Added property", schema.getIncreasedProperties(), schema.getContext()); + + propertiesChanged( + output, prefix, "Changed property", schema.getChangedProperties(), schema.getContext()); + if (schema.getItems() != null) { + itemsAllChanges(output, propName, schema.getItems()); + } + schema + .getChangedProperties() + .forEach((name, property) -> allChanges(output, prefix + name, property)); + } + private void incompatibilities(final LiTag output, final ChangedSchema schema) { incompatibilities(output, "", schema); } @@ -299,6 +406,10 @@ private void items(ContainerTag<?> output, String propName, ChangedSchema schema incompatibilities(output, propName + "[n]", schema); } + private void itemsAllChanges(ContainerTag<?> output, String propName, ChangedSchema schema) { + allChanges(output, propName + "[n]", schema); + } + private void properties( ContainerTag<?> output, String propPrefix, @@ -310,6 +421,17 @@ private void properties( } } + private void propertiesChanged( + ContainerTag<?> output, + String propPrefix, + String title, + Map<String, ChangedSchema> properties, + DiffContext context) { + if (properties != null) { + properties.forEach((key, value) -> resolveProperty(output, propPrefix, key, value, title)); + } + } + private void resolveProperty( ContainerTag<?> output, String propPrefix, String key, Schema<?> value, String title) { try { @@ -319,6 +441,15 @@ private void resolveProperty( } } + private void resolveProperty( + ContainerTag<?> output, String propPrefix, String key, ChangedSchema value, String title) { + try { + property(output, propPrefix + key, title, resolve(value)); + } catch (Exception e) { + property(output, propPrefix + key, title, type(value)); + } + } + protected void property(ContainerTag<?> output, String name, String title, Schema<?> schema) { property(output, name, title, type(schema)); } @@ -332,6 +463,13 @@ protected Schema<?> resolve(Schema<?> schema) { diff.getNewSpecOpenApi().getComponents(), schema, schema.get$ref()); } + protected Schema<?> resolve(ChangedSchema schema) { + return refPointer.resolveRef( + diff.getNewSpecOpenApi().getComponents(), + schema.getNewSchema(), + schema.getNewSchema().get$ref()); + } + protected String type(Schema<?> schema) { String result = "object"; if (schema == null) { @@ -344,6 +482,10 @@ protected String type(Schema<?> schema) { return result; } + protected String type(ChangedSchema schema) { + return type(schema.getNewSchema()); + } + private UlTag ul_param(ChangedParameters changedParameters) { List<Parameter> addParameters = changedParameters.getIncreased(); List<Parameter> delParameters = changedParameters.getMissing(); @@ -400,10 +542,14 @@ private LiTag li_changedParam(ChangedParameter changeParam) { .map(ChangedMetadata::isDifferent) .orElse(false); Parameter rightParam = changeParam.getNewParameter(); - Parameter leftParam = changeParam.getNewParameter(); + Parameter leftParam = changeParam.getOldParameter(); LiTag li = li().withText(changeParam.getName() + " in " + changeParam.getIn()); if (changeRequired) { - li.withText(" change into " + (rightParam.getRequired() ? "required" : "not required")); + li.withText( + " change into " + + (rightParam.getRequired() != null && rightParam.getRequired() + ? "required" + : "not required")); } if (changeDescription) { li.withText(" Notes ") diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/HttpStatus.java b/core/src/main/java/org/openapitools/openapidiff/core/output/HttpStatus.java new file mode 100644 index 000000000..13a81c324 --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/HttpStatus.java @@ -0,0 +1,112 @@ +/* + * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpStatus.java,v 1.18 2004/05/02 11:21:13 olegk Exp $ + * $Revision: 480424 $ + * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ + * + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +package org.openapitools.openapidiff.core.output; + +import java.util.HashMap; +import java.util.Map; + +/** + * Constants enumerating the HTTP status codes. All status codes defined in RFC1945 (HTTP/1.0, + * RFC2616 (HTTP/1.1), and RFC2518 (WebDAV) are supported. + */ +public final class HttpStatus { + + private static final Map<Integer, String> REASON_PHRASES = new HashMap<>(); + + static { + REASON_PHRASES.put(100, "Continue"); + REASON_PHRASES.put(101, "Switching Protocols"); + REASON_PHRASES.put(102, "Processing"); + REASON_PHRASES.put(200, "OK"); + REASON_PHRASES.put(201, "Created"); + REASON_PHRASES.put(202, "Accepted"); + REASON_PHRASES.put(203, "Non Authoritative Information"); + REASON_PHRASES.put(204, "No Content"); + REASON_PHRASES.put(205, "Reset Content"); + REASON_PHRASES.put(206, "Partial Content"); + REASON_PHRASES.put(207, "Multi-Status"); + REASON_PHRASES.put(300, "Multiple Choices"); + REASON_PHRASES.put(301, "Moved Permanently"); + REASON_PHRASES.put(302, "Moved Temporarily"); + REASON_PHRASES.put(303, "See Other"); + REASON_PHRASES.put(304, "Not Modified"); + REASON_PHRASES.put(305, "Use Proxy"); + REASON_PHRASES.put(307, "Temporary Redirect"); + REASON_PHRASES.put(400, "Bad Request"); + REASON_PHRASES.put(401, "Unauthorized"); + REASON_PHRASES.put(402, "Payment Required"); + REASON_PHRASES.put(403, "Forbidden"); + REASON_PHRASES.put(404, "Not Found"); + REASON_PHRASES.put(405, "Method Not Allowed"); + REASON_PHRASES.put(406, "Not Acceptable"); + REASON_PHRASES.put(407, "Proxy Authentication Required"); + REASON_PHRASES.put(408, "Request Timeout"); + REASON_PHRASES.put(409, "Conflict"); + REASON_PHRASES.put(410, "Gone"); + REASON_PHRASES.put(411, "Length Required"); + REASON_PHRASES.put(412, "Precondition Failed"); + REASON_PHRASES.put(413, "Request Too Long"); + REASON_PHRASES.put(414, "Request-URI Too Long"); + REASON_PHRASES.put(415, "Unsupported Media Type"); + REASON_PHRASES.put(416, "Requested Range Not Satisfiable"); + REASON_PHRASES.put(417, "Expectation Failed"); + REASON_PHRASES.put(419, "Insufficient Space On Resource"); + REASON_PHRASES.put(420, "Method Failure"); + REASON_PHRASES.put(422, "Unprocessable Entity"); + REASON_PHRASES.put(423, "Locked"); + REASON_PHRASES.put(424, "Failed Dependency"); + REASON_PHRASES.put(500, "Internal Server Error"); + REASON_PHRASES.put(501, "Not Implemented"); + REASON_PHRASES.put(502, "Bad Gateway"); + REASON_PHRASES.put(503, "Service Unavailable"); + REASON_PHRASES.put(504, "Gateway Timeout"); + REASON_PHRASES.put(505, "Http Version Not Supported"); + REASON_PHRASES.put(507, "Insufficient Storage"); + } + + /** + * Get the reason phrase for a particular status code. + * + * <p>This method always returns the English text as specified in the relevant RFCs and is not + * internationalized. + * + * @param statusCode the numeric status code + * @return the reason phrase associated with the given status code or null if the status code is + * not recognized. + */ + public static String getReasonPhrase(int statusCode) { + if (statusCode < 0) { + throw new IllegalArgumentException("status code may not be negative"); + } + return REASON_PHRASES.get(statusCode); + } + + private HttpStatus() {} +} diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java index b0518d000..b6629d5f9 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java @@ -1,18 +1,31 @@ package org.openapitools.openapidiff.core.output; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.OutputStreamWriter; +import org.openapitools.openapidiff.core.exception.RendererException; import org.openapitools.openapidiff.core.model.ChangedOpenApi; public class JsonRender implements Render { - private final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper; + + public JsonRender() { + objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.findAndRegisterModules(); + } @Override - public String render(ChangedOpenApi diff) { + public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) { try { - return objectMapper.writeValueAsString(diff); + objectMapper.writeValue(outputStreamWriter, diff); + outputStreamWriter.close(); } catch (JsonProcessingException e) { - throw new RuntimeException("Could not serialize diff as JSON", e); + throw new RendererException("Could not serialize diff as JSON", e); + } catch (IOException e) { + throw new RendererException(e); } } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java index fd6726615..e55179b15 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java @@ -11,11 +11,16 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.HashSet; import java.util.List; import java.util.Map; -import org.apache.commons.httpclient.HttpStatus; +import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.openapitools.openapidiff.core.exception.RendererException; import org.openapitools.openapidiff.core.model.*; +import org.openapitools.openapidiff.core.model.schema.ChangedOneOfSchema; import org.openapitools.openapidiff.core.utils.RefPointer; import org.openapitools.openapidiff.core.utils.RefType; import org.slf4j.Logger; @@ -36,33 +41,40 @@ public class MarkdownRender implements Render { protected RefPointer<Schema<?>> refPointer = new RefPointer<>(RefType.SCHEMAS); protected ChangedOpenApi diff; + protected Set<Schema<?>> handledSchemas = new HashSet<>(); /** * A parameter which indicates whether or not metadata (summary and metadata) changes should be * logged in the changelog file. */ protected boolean showChangedMetadata; - public String render(ChangedOpenApi diff) { + public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) { this.diff = diff; - return listEndpoints("What's New", diff.getNewEndpoints()) - + listEndpoints("What's Deleted", diff.getMissingEndpoints()) - + listEndpoints("What's Deprecated", diff.getDeprecatedEndpoints()) - + listEndpoints(diff.getChangedOperations()); + this.handledSchemas.clear(); + listEndpoints("What's New", diff.getNewEndpoints(), outputStreamWriter); + listEndpoints("What's Deleted", diff.getMissingEndpoints(), outputStreamWriter); + listEndpoints("What's Deprecated", diff.getDeprecatedEndpoints(), outputStreamWriter); + listEndpoints(diff.getChangedOperations(), outputStreamWriter); + try { + outputStreamWriter.close(); + } catch (IOException e) { + throw new RendererException(e); + } } protected String sectionTitle(String title) { return H4 + title + '\n' + HR + '\n'; } - protected String listEndpoints(String title, List<Endpoint> endpoints) { + protected void listEndpoints( + String title, List<Endpoint> endpoints, OutputStreamWriter outputStreamWriter) { if (null == endpoints || endpoints.isEmpty()) { - return ""; + return; } - StringBuilder sb = new StringBuilder(sectionTitle(title)); + safelyAppend(outputStreamWriter, sectionTitle(title)); endpoints.stream() .map(e -> itemEndpoint(e.getMethod().toString(), e.getPathUrl(), e.getSummary())) - .forEach(sb::append); - return sb.toString(); + .forEach(csq -> safelyAppend(outputStreamWriter, csq)); } protected String itemEndpoint(String method, String path, String summary) { @@ -77,41 +89,36 @@ protected String titleH5(String title) { return H6 + title + '\n'; } - protected String listEndpoints(List<ChangedOperation> changedOperations) { + protected void listEndpoints( + List<ChangedOperation> changedOperations, OutputStreamWriter outputStreamWriter) { if (null == changedOperations || changedOperations.isEmpty()) { - return ""; + return; } - StringBuilder sb = new StringBuilder(sectionTitle("What's Changed")); - changedOperations.stream() - .map( - operation -> { - StringBuilder details = - new StringBuilder() - .append( - itemEndpoint( - operation.getHttpMethod().toString(), - operation.getPathUrl(), - operation.getSummary())); - if (result(operation.getParameters()).isDifferent()) { - details - .append(titleH5("Parameters:")) - .append(parameters(operation.getParameters())); - } - if (operation.resultRequestBody().isDifferent()) { - details - .append(titleH5("Request:")) - .append(metadata("Description", operation.getRequestBody().getDescription())) - .append(bodyContent(operation.getRequestBody().getContent())); - } - if (operation.resultApiResponses().isDifferent()) { - details - .append(titleH5("Return Type:")) - .append(responses(operation.getApiResponses())); - } - return details.toString(); - }) - .forEach(sb::append); - return sb.toString(); + safelyAppend(outputStreamWriter, sectionTitle("What's Changed")); + changedOperations.forEach( + operation -> { + safelyAppend( + outputStreamWriter, + itemEndpoint( + operation.getHttpMethod().toString(), + operation.getPathUrl(), + operation.getSummary())); + if (result(operation.getParameters()).isDifferent()) { + safelyAppend(outputStreamWriter, titleH5("Parameters:")); + safelyAppend(outputStreamWriter, parameters(operation.getParameters())); + } + if (operation.resultRequestBody().isDifferent()) { + safelyAppend(outputStreamWriter, titleH5("Request:")); + safelyAppend( + outputStreamWriter, + metadata("Description", operation.getRequestBody().getDescription())); + safelyAppend(outputStreamWriter, bodyContent(operation.getRequestBody().getContent())); + } + if (operation.resultApiResponses().isDifferent()) { + safelyAppend(outputStreamWriter, titleH5("Return Type:")); + safelyAppend(outputStreamWriter, responses(operation.getApiResponses())); + } + }); } protected String responses(ChangedApiResponse changedApiResponse) { @@ -155,8 +162,8 @@ protected String itemResponse(String code, ChangedResponse response) { protected String itemResponse(String title, String code, String description) { StringBuilder sb = new StringBuilder(); String status = ""; - if (!code.equals("default")) { - status = HttpStatus.getStatusText(Integer.parseInt(code)); + if (!code.equals("default") && !code.matches("[1-5]XX")) { + status = HttpStatus.getReasonPhrase(Integer.parseInt(code)); } sb.append(format("%s : **%s %s**\n", title, code, status)); sb.append(metadata(description)); @@ -336,6 +343,8 @@ protected String schema(int deepness, ComposedSchema schema, DiffContext context } protected String schema(int deepness, Schema schema, DiffContext context) { + if (handledSchemas.contains(schema)) return ""; + handledSchemas.add(schema); StringBuilder sb = new StringBuilder(); sb.append(listItem(deepness, "Enum", schema.getEnum())); sb.append(properties(deepness, "Property", schema.getProperties(), true, context)); @@ -523,7 +532,7 @@ protected String blockquote(String beginning) { protected String blockquote(String beginning, String text) { String blockquote = blockquote(beginning); - return blockquote + text.trim().replace("\n", "\n" + blockquote) + '\n'; + return blockquote + text.trim().replace("\n", "\n" + blockquote) + "\n\n"; } protected String type(Schema<?> schema) { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java b/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java index 4a48ebcfa..529a290b9 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java @@ -1,8 +1,51 @@ package org.openapitools.openapidiff.core.output; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import org.openapitools.openapidiff.core.exception.RendererException; import org.openapitools.openapidiff.core.model.ChangedOpenApi; public interface Render { - String render(ChangedOpenApi diff); + /** + * render provided diff object + * + * @param diff diff object to render + * @param outputStreamWriter writer for rendered results + */ + void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) throws RendererException; + + /** + * render provided diff object + * + * @deprecated since 2.1.0, use {@link Render#render(ChangedOpenApi, OutputStreamWriter)} to avoid + * massive String output issues. details <a + * href="https://github.com/OpenAPITools/openapi-diff/issues/543">#543</a> + * @param diff diff object to render + * @return rendered output + */ + @Deprecated + default String render(ChangedOpenApi diff) throws RendererException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream); + render(diff, outputStreamWriter); + String result = byteArrayOutputStream.toString(); + + try { + outputStreamWriter.close(); + } catch (IOException e) { + throw new RendererException(e); + } + + return result; + } + + default void safelyAppend(OutputStreamWriter outputStreamWriter, String csq) { + try { + outputStreamWriter.append(csq); + } catch (IOException ex) { + throw new RendererException(ex); + } + } } diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java index 27a5e0e20..6da806a96 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java @@ -5,7 +5,9 @@ public class ChangedUtils { - private ChangedUtils() {} + private ChangedUtils() { + throw new UnsupportedOperationException("Utility class. Do not instantiate"); + } public static boolean isUnchanged(Changed changed) { return changed == null || changed.isUnchanged(); diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java index 4179fd359..73c9a073b 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java @@ -5,7 +5,9 @@ public class Copy { - private Copy() {} + private Copy() { + throw new UnsupportedOperationException("Utility class. Do not instantiate"); + } public static <K, V> Map<K, V> copyMap(Map<K, V> map) { if (map == null) { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java index 749b0bdbd..c749d8a54 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java @@ -10,7 +10,9 @@ public class EndpointUtils { - private EndpointUtils() {} + private EndpointUtils() { + throw new UnsupportedOperationException("Utility class. Do not instantiate"); + } public static Collection<Endpoint> convert2Endpoints( String pathUrl, Map<PathItem.HttpMethod, Operation> map) { diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/FileUtils.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/FileUtils.java new file mode 100644 index 000000000..a2c8b147e --- /dev/null +++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/FileUtils.java @@ -0,0 +1,35 @@ +package org.openapitools.openapidiff.core.utils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.output.Render; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class FileUtils { + private static final Logger logger = LoggerFactory.getLogger(FileUtils.class); + + private FileUtils() { + throw new UnsupportedOperationException("Utility class. Do not instantiate"); + } + + public static void writeToFile( + final Render render, final ChangedOpenApi diff, final String fileName) { + if (fileName == null || fileName.isEmpty()) { + logger.debug("File name cannot be null or empty."); + return; + } + + final Path filePath = Paths.get(fileName); + try (final FileOutputStream outputStream = new FileOutputStream(filePath.toFile()); + final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) { + render.render(diff, outputStreamWriter); + } catch (final IOException e) { + logger.error("Exception while writing to file {}", fileName, e); + } + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AddPropDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AddPropDiffTest.java deleted file mode 100644 index b71d8f039..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/AddPropDiffTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals; -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible; - -import org.junit.jupiter.api.Test; - -public class AddPropDiffTest { - private final String OPENAPI_DOC1 = "add-prop-1.yaml"; - private final String OPENAPI_DOC2 = "add-prop-2.yaml"; - - @Test - public void testDiffSame() { - assertOpenApiAreEquals(OPENAPI_DOC1, OPENAPI_DOC1); - } - - @Test - public void testDiffDifferent() { - assertOpenApiBackwardIncompatible(OPENAPI_DOC1, OPENAPI_DOC2); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AddPropPutDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AddPropPutDiffTest.java deleted file mode 100644 index d73ab6ebe..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/AddPropPutDiffTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals; -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible; - -import org.junit.jupiter.api.Test; - -public class AddPropPutDiffTest { - private final String OPENAPI_DOC1 = "add-prop-put-1.yaml"; - private final String OPENAPI_DOC2 = "add-prop-put-2.yaml"; - - @Test - public void testDiffSame() { - assertOpenApiAreEquals(OPENAPI_DOC1, OPENAPI_DOC1); - } - - @Test - public void testDiffDifferent() { - assertOpenApiBackwardIncompatible(OPENAPI_DOC1, OPENAPI_DOC2); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AdditionalPropertiesTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AdditionalPropertiesTest.java deleted file mode 100644 index df7885e08..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/AdditionalPropertiesTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.output.ConsoleRender; - -class AdditionalPropertiesTest { - @Test - void booleanAdditionalPropertiesAreSupported() { - ChangedOpenApi diff = OpenApiCompare.fromLocations("issue-256_1.json", "issue-256_2.json"); - ConsoleRender render = new ConsoleRender(); - assertThat(render.render(diff)).isNotBlank(); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AllOfOneOfDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AllOfOneOfDiffTest.java new file mode 100644 index 000000000..ec1bcfc53 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/AllOfOneOfDiffTest.java @@ -0,0 +1,14 @@ +package org.openapitools.openapidiff.core; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class AllOfOneOfDiffTest { + @Test + void allOfReferringToOneOfSchemasAreSupported() { + ChangedOpenApi diff = OpenApiCompare.fromLocations("issue-317_1.json", "issue-317_2.json"); + assertThat(diff.isCoreChanged().isUnchanged()); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/BackwardCompatibilityTest.java b/core/src/test/java/org/openapitools/openapidiff/core/BackwardCompatibilityTest.java deleted file mode 100644 index 612700ca7..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/BackwardCompatibilityTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible; -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible; - -import org.junit.jupiter.api.Test; - -public class BackwardCompatibilityTest { - private final String OPENAPI_DOC1 = "backwardCompatibility/bc_1.yaml"; - private final String OPENAPI_DOC2 = "backwardCompatibility/bc_2.yaml"; - private final String OPENAPI_DOC3 = "backwardCompatibility/bc_3.yaml"; - private final String OPENAPI_DOC4 = "backwardCompatibility/bc_4.yaml"; - private final String OPENAPI_DOC5 = "backwardCompatibility/bc_5.yaml"; - - @Test - public void testNoChange() { - assertOpenApiBackwardCompatible(OPENAPI_DOC1, OPENAPI_DOC1, false); - } - - @Test - public void testApiAdded() { - assertOpenApiBackwardCompatible(OPENAPI_DOC1, OPENAPI_DOC2, true); - } - - @Test - public void testApiMissing() { - assertOpenApiBackwardIncompatible(OPENAPI_DOC2, OPENAPI_DOC1); - } - - @Test - public void testApiChangedOperationAdded() { - assertOpenApiBackwardCompatible(OPENAPI_DOC2, OPENAPI_DOC3, true); - } - - @Test - public void testApiChangedOperationMissing() { - assertOpenApiBackwardIncompatible(OPENAPI_DOC3, OPENAPI_DOC2); - } - - @Test - public void testApiOperationChanged() { - assertOpenApiBackwardCompatible(OPENAPI_DOC2, OPENAPI_DOC4, true); - } - - @Test - public void testApiReadWriteOnlyPropertiesChanged() { - assertOpenApiBackwardCompatible(OPENAPI_DOC1, OPENAPI_DOC5, true); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ChangesResolver.java b/core/src/test/java/org/openapitools/openapidiff/core/ChangesResolver.java new file mode 100644 index 000000000..cb0fe37b6 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/ChangesResolver.java @@ -0,0 +1,113 @@ +package org.openapitools.openapidiff.core; + +import io.swagger.v3.oas.models.PathItem.HttpMethod; +import java.util.Optional; +import javax.annotation.Nullable; +import org.openapitools.openapidiff.core.model.ChangedApiResponse; +import org.openapitools.openapidiff.core.model.ChangedContent; +import org.openapitools.openapidiff.core.model.ChangedHeaders; +import org.openapitools.openapidiff.core.model.ChangedMediaType; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.model.ChangedOperation; +import org.openapitools.openapidiff.core.model.ChangedParameter; +import org.openapitools.openapidiff.core.model.ChangedParameters; +import org.openapitools.openapidiff.core.model.ChangedRequestBody; +import org.openapitools.openapidiff.core.model.ChangedResponse; +import org.openapitools.openapidiff.core.model.ChangedSchema; + +public class ChangesResolver { + + /** + * Get the ChangedOperation for the given method and path. + * + * @param changedOpenApi the ChangedOpenApi object + * @param method the HTTP method + * @param path the path + * @return the ChangedOperation object + */ + @Nullable + public static ChangedOperation getChangedOperation( + ChangedOpenApi changedOpenApi, HttpMethod method, String path) { + return changedOpenApi.getChangedOperations().stream() + .filter( + operation -> + operation.getHttpMethod().equals(method) && operation.getPathUrl().equals(path)) + .findFirst() + .orElse(null); + } + + /** + * Get the ChangedParameter for the given method, path, and parameter name. + * + * @param changedOpenApi the ChangedOpenApi object + * @param method the HTTP method + * @param path the path + * @param parameterName the parameter name + * @return the ChangedParameter object + */ + @Nullable + public static ChangedParameter getChangedParameter( + ChangedOpenApi changedOpenApi, HttpMethod method, String path, String parameterName) { + ChangedOperation changedOperation = getChangedOperation(changedOpenApi, method, path); + + if (changedOperation == null) { + return null; + } + + return Optional.ofNullable(changedOperation.getParameters()) + .map(ChangedParameters::getChanged) + .flatMap( + changedParameters -> + changedParameters.stream() + .filter(changedParameter -> changedParameter.getName().equals(parameterName)) + .findFirst()) + .orElse(null); + } + + /** + * Get the ChangedHeaders for the given method, path, and response code. + * + * @param changedOpenApi the ChangedOpenApi object + * @param method the HTTP method + * @param path the path + * @param responseCode the response code + * @return the ChangedHeaders object + */ + @Nullable + public static ChangedHeaders getChangedResponseHeaders( + ChangedOpenApi changedOpenApi, HttpMethod method, String path, String responseCode) { + return Optional.ofNullable(getChangedOperation(changedOpenApi, method, path)) + .map(ChangedOperation::getApiResponses) + .map(ChangedApiResponse::getChanged) + .map(responses -> responses.get(responseCode)) + .map(ChangedResponse::getHeaders) + .orElse(null); + } + + /** + * Get the ChangedSchema for the given method, path, and media type. + * + * @param changedOpenApi the ChangedOpenApi object + * @param method the HTTP method + * @param path the path + * @param mediaType the media type + * @return the ChangedSchema object + */ + @Nullable + public static ChangedSchema getRequestBodyChangedSchema( + ChangedOpenApi changedOpenApi, HttpMethod method, String path, String mediaType) { + ChangedOperation changedOperation = getChangedOperation(changedOpenApi, method, path); + + if (changedOperation == null) { + return null; + } + + return Optional.ofNullable(changedOperation) + .map(ChangedOperation::getRequestBody) + .map(ChangedRequestBody::getContent) + .map(ChangedContent::getChanged) + .map(changedMediaTypes -> changedMediaTypes.get(mediaType)) + .map(ChangedMediaType::getSchema) + .orElse(null); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ConsoleRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ConsoleRenderTest.java deleted file mode 100644 index 329fea054..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/ConsoleRenderTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.output.ConsoleRender; - -public class ConsoleRenderTest { - @Test - public void renderDoesNotFailWhenPropertyHasBeenRemoved() { - ConsoleRender render = new ConsoleRender(); - ChangedOpenApi diff = - OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); - assertThat(render.render(diff)).isNotBlank(); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java index 190b5a32f..e3cf03534 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java @@ -33,7 +33,7 @@ public void testAddedResponseContentTypeDiff() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations( "content_type_response_add_1.yaml", "content_type_response_add_2.yaml"); - assertThat(changedOpenApi.isCompatible()).isFalse(); + assertThat(changedOpenApi.isCompatible()).isTrue(); } @Test @@ -41,7 +41,7 @@ public void testRemovedResponseContentTypeDiff() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations( "content_type_response_add_2.yaml", "content_type_response_add_1.yaml"); - assertThat(changedOpenApi.isCompatible()).isTrue(); + assertThat(changedOpenApi.isCompatible()).isFalse(); } @Test diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ExamplesDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ExamplesDiffTest.java new file mode 100644 index 000000000..1743de4d1 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/ExamplesDiffTest.java @@ -0,0 +1,167 @@ +package org.openapitools.openapidiff.core; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.openapitools.openapidiff.core.TestUtils.getOpenApiChangedOperations; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.ChangedOperation; + +public class ExamplesDiffTest { + + @Test + public void issue666OAS30ExampleInMediaType() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-0-examples-handling-1.yaml", + "issue-666-oas-3-0-examples-handling-2.yaml"); + + ChangedOperation requestMediaTypeExampleChanged = + changedOperations.stream() + .filter(o -> "/example/request/media-type".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + ChangedOperation responseMediaTypeExampleChanged = + changedOperations.stream() + .filter(o -> "/example/response/media-type".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(requestMediaTypeExampleChanged).isNotNull(); + assertThat(responseMediaTypeExampleChanged).isNotNull(); + } + + @Test + public void issue666OAS31ExamplesInMediaType() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-1-examples-handling-1.yaml", + "issue-666-oas-3-1-examples-handling-2.yaml"); + + ChangedOperation requestMediaTypeExamplesChanged = + changedOperations.stream() + .filter(o -> "/examples/request/media-type".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + ChangedOperation responseMediaTypeExamplesChanged = + changedOperations.stream() + .filter(o -> "/examples/response/media-type".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(requestMediaTypeExamplesChanged).isNotNull(); + assertThat(responseMediaTypeExamplesChanged).isNotNull(); + } + + @Test + public void issue666OAS30ExampleInParameter() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-0-examples-handling-1.yaml", + "issue-666-oas-3-0-examples-handling-2.yaml"); + + ChangedOperation paramExampleChanged = + changedOperations.stream() + .filter(o -> "/example/parameter/{id}".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(paramExampleChanged).isNotNull(); + } + + @Test + public void issue666OAS31ExamplesInParameter() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-1-examples-handling-1.yaml", + "issue-666-oas-3-1-examples-handling-2.yaml"); + + ChangedOperation paramExamplesChanged = + changedOperations.stream() + .filter(o -> "/examples/parameter/{id}".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(paramExamplesChanged).isNotNull(); + } + + @Test + public void issue666OAS30ExampleInResponseHeader() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-0-examples-handling-1.yaml", + "issue-666-oas-3-0-examples-handling-2.yaml"); + + ChangedOperation operation = + changedOperations.stream() + .filter(o -> "/example/response/header".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(operation).isNotNull(); + } + + @Test + public void issue666OAS31ExamplesInResponseHeader() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-1-examples-handling-1.yaml", + "issue-666-oas-3-1-examples-handling-2.yaml"); + + ChangedOperation operation = + changedOperations.stream() + .filter(o -> "/examples/response/header".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(operation).isNotNull(); + } + + @Test + public void issue666OAS30ExampleInSchema() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-0-examples-handling-1.yaml", + "issue-666-oas-3-0-examples-handling-2.yaml"); + + ChangedOperation operation = + changedOperations.stream() + .filter(o -> "/example/model/property".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(operation).isNotNull(); + } + + @Test + public void issue666OAS31ExamplesInSchema() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-1-examples-handling-1.yaml", + "issue-666-oas-3-1-examples-handling-2.yaml"); + + ChangedOperation operation = + changedOperations.stream() + .filter(o -> "/examples/model/property".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(operation).isNotNull(); + } + + @Test + public void issue666OAS30ExampleInComposedSchema() { + List<ChangedOperation> changedOperations = + getOpenApiChangedOperations( + "issue-666-oas-3-0-examples-handling-1.yaml", + "issue-666-oas-3-0-examples-handling-2.yaml"); + + ChangedOperation operation = + changedOperations.stream() + .filter(o -> "/example/model/property/composed".equals(o.getPathUrl())) + .findFirst() + .orElse(null); + + assertThat(operation).isNotNull(); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/HtmlRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/HtmlRenderTest.java deleted file mode 100644 index 6273ef243..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/HtmlRenderTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.output.HtmlRender; - -public class HtmlRenderTest { - @Test - public void renderDoesNotFailWhenPropertyHasBeenRemoved() { - HtmlRender render = new HtmlRender(); - ChangedOpenApi diff = - OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); - assertThat(render.render(diff)).isNotBlank(); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/JsonRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/JsonRenderTest.java deleted file mode 100644 index 547ced027..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/JsonRenderTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.output.JsonRender; - -public class JsonRenderTest { - @Test - public void renderDoesNotFailWhenPropertyHasBeenRemoved() { - JsonRender render = new JsonRender(); - ChangedOpenApi diff = - OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); - assertThat(render.render(diff)).isNotBlank(); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/MarkdownRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/MarkdownRenderTest.java deleted file mode 100644 index c92a1cd8c..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/MarkdownRenderTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.openapitools.openapidiff.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; -import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.output.MarkdownRender; - -public class MarkdownRenderTest { - @Test - public void renderDoesNotFailWhenPropertyHasBeenRemoved() { - MarkdownRender render = new MarkdownRender(); - ChangedOpenApi diff = - OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); - assertThat(render.render(diff)).isNotBlank(); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java index 88ac57b11..ea21bd282 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java @@ -3,24 +3,30 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Path; +import io.swagger.parser.OpenAPIParser; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.parser.core.models.ParseOptions; +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; import java.util.List; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.openapitools.openapidiff.core.model.ChangedOpenApi; import org.openapitools.openapidiff.core.model.ChangedOperation; +import org.openapitools.openapidiff.core.model.DiffResult; import org.openapitools.openapidiff.core.model.Endpoint; import org.openapitools.openapidiff.core.output.HtmlRender; import org.openapitools.openapidiff.core.output.JsonRender; import org.openapitools.openapidiff.core.output.MarkdownRender; +import org.openapitools.openapidiff.core.output.Render; public class OpenApiDiffTest { private final String OPENAPI_DOC1 = "petstore_v2_1.yaml"; private final String OPENAPI_DOC2 = "petstore_v2_2.yaml"; private final String OPENAPI_EMPTY_DOC = "petstore_v2_empty.yaml"; + private final String OPENAPI_DOC3 = "petstore_openapi3.yaml"; + + private static final OpenAPIParser PARSER = new OpenAPIParser(); @Test public void testEqual() { @@ -28,7 +34,7 @@ public void testEqual() { } @Test - public void testNewApi(@TempDir Path tempDir) throws IOException { + public void testNewApi() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_EMPTY_DOC, OPENAPI_DOC2); List<Endpoint> newEndpoints = changedOpenApi.getNewEndpoints(); List<Endpoint> missingEndpoints = changedOpenApi.getMissingEndpoints(); @@ -37,18 +43,16 @@ public void testNewApi(@TempDir Path tempDir) throws IOException { assertThat(missingEndpoints).isEmpty(); assertThat(changedEndPoints).isEmpty(); - String html = - new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(changedOpenApi); - final Path path = tempDir.resolve("testNewApi.html"); - try (FileWriter fw = new FileWriter(path.toFile())) { - fw.write(html); - } - assertThat(path).isNotEmptyFile(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + Render render = + new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); + render.render(changedOpenApi, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); } @Test - public void testDeprecatedApi(@TempDir Path tempDir) throws IOException { + public void testDeprecatedApi() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_EMPTY_DOC); List<Endpoint> newEndpoints = changedOpenApi.getNewEndpoints(); List<Endpoint> missingEndpoints = changedOpenApi.getMissingEndpoints(); @@ -57,51 +61,82 @@ public void testDeprecatedApi(@TempDir Path tempDir) throws IOException { assertThat(missingEndpoints).isNotEmpty(); assertThat(changedEndPoints).isEmpty(); - String html = - new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(changedOpenApi); - final Path path = tempDir.resolve("testDeprecatedApi.html"); - try (FileWriter fw = new FileWriter(path.toFile())) { - fw.write(html); - } - assertThat(path).isNotEmptyFile(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + Render render = + new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); + render.render(changedOpenApi, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); } @Test - public void testDiff(@TempDir Path tempDir) throws IOException { + public void testDiff() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); List<ChangedOperation> changedEndPoints = changedOpenApi.getChangedOperations(); assertThat(changedEndPoints).isNotEmpty(); - String html = - new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css") - .render(changedOpenApi); - final Path path = tempDir.resolve("testDiff.html"); - try (FileWriter fw = new FileWriter(path.toFile())) { - fw.write(html); - } - assertThat(path).isNotEmptyFile(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + Render render = + new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css"); + render.render(changedOpenApi, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); } @Test - public void testDiffAndMarkdown(@TempDir Path tempDir) throws IOException { + public void testDiffAndMarkdown() { ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); - String render = new MarkdownRender().render(diff); - final Path path = tempDir.resolve("testDiff.md"); - try (FileWriter fw = new FileWriter(path.toFile())) { - fw.write(render); - } - assertThat(path).isNotEmptyFile(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + Render render = new MarkdownRender(); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); } @Test - public void testDiffAndJson(@TempDir Path tempDir) throws IOException { + public void testDiffAndJson() { ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); - String render = new JsonRender().render(diff); - final Path path = tempDir.resolve("testDiff.json"); - try (FileWriter fw = new FileWriter(path.toFile())) { - fw.write(render); - } - assertThat(path).isNotEmptyFile(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + Render render = new JsonRender(); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + /** Testing that repetitive specs comparisons has to produce consistent result. */ + @Test + public void testComparisonConsistency() { + final OpenAPI oldSpec = loadSpecFromFile(OPENAPI_DOC3); + final OpenAPI newSpec = loadSpecFromFile(OPENAPI_DOC3); + + final ChangedOpenApi diff1 = OpenApiCompare.fromSpecifications(oldSpec, newSpec); + assertThat(diff1.isChanged()).isEqualTo(DiffResult.NO_CHANGES); + assertThat(diff1.getNewEndpoints()).isEmpty(); + assertThat(diff1.getMissingEndpoints()).isEmpty(); + assertThat(diff1.getChangedOperations()).isEmpty(); + + final ChangedOpenApi diff2 = OpenApiCompare.fromSpecifications(oldSpec, newSpec); + assertThat(diff2.isChanged()).isEqualTo(DiffResult.NO_CHANGES); + assertThat(diff2.getNewEndpoints()).isEmpty(); + assertThat(diff2.getMissingEndpoints()).isEmpty(); + assertThat(diff2.getChangedOperations()).isEmpty(); + } + + @Test + public void testSpecObjectsAreNotChangesAfterComparison() { + final OpenAPI oldSpec = loadSpecFromFile(OPENAPI_DOC3); + final OpenAPI newSpec = loadSpecFromFile(OPENAPI_DOC3); + + OpenApiCompare.fromSpecifications(oldSpec, newSpec); + OpenApiCompare.fromSpecifications(oldSpec, newSpec); + + final OpenAPI expectedOldSpec = loadSpecFromFile(OPENAPI_DOC3); + final OpenAPI expectedNewSpec = loadSpecFromFile(OPENAPI_DOC3); + assertThat(oldSpec).isEqualTo(expectedOldSpec); + assertThat(newSpec).isEqualTo(expectedNewSpec); + } + + private static OpenAPI loadSpecFromFile(String specFile) { + return PARSER.readLocation(specFile, null, new ParseOptions()).getOpenAPI(); } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java index 79f929f82..4a1794204 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java @@ -1,23 +1,69 @@ package org.openapitools.openapidiff.core; +import static io.swagger.v3.oas.models.PathItem.HttpMethod.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.openapitools.openapidiff.core.ChangesResolver.getChangedOperation; import org.junit.jupiter.api.Test; import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.model.ChangedOperation; import org.openapitools.openapidiff.core.model.DiffResult; public class OperationDiffTest { - private final String OPENAPI_DOC1 = "operation_diff_1.yaml"; - private final String OPENAPI_DOC2 = "operation_diff_2.yaml"; + private final String OPENAPI_DOC1 = "operationDiff/operation_diff_1.yaml"; + private final String OPENAPI_DOC2 = "operationDiff/operation_diff_2.yaml"; @Test - public void testContentDiffWithOneEmptyMediaType() { + public void testOperationIdChanged() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); - assertThat(changedOpenApi.isChanged()).isEqualTo(DiffResult.METADATA); - assertThat(changedOpenApi.isDifferent()).isTrue(); - assertThat(changedOpenApi.getChangedOperations().size()).isEqualTo(1); - assertThat(changedOpenApi.getChangedOperations().get(0).getOperationId().isDifferent()) - .isTrue(); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/operation/operation-id"); + + assertThat(changedOperation).isNotNull(); + assertThat(changedOperation.isChanged()).isEqualTo(DiffResult.METADATA); + assertThat(changedOperation.getOperationId().getRight()).isEqualTo("changed"); + } + + @Test + public void testOperationSummaryChanged() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/operation/summary"); + + assertThat(changedOperation).isNotNull(); + assertThat(changedOperation.isChanged()).isEqualTo(DiffResult.METADATA); + assertThat(changedOperation.getSummary().getRight()).isEqualTo("changed"); + } + + @Test + public void testOperationDescriptionChanged() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/operation/description"); + + assertThat(changedOperation).isNotNull(); + assertThat(changedOperation.isChanged()).isEqualTo(DiffResult.METADATA); + assertThat(changedOperation.getDescription().getRight()).isEqualTo("changed"); + } + + @Test + public void testOperationBecomesDeprecated() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/operation/becomes-deprecated"); + + assertThat(changedOperation).isNotNull(); + assertThat(changedOperation.isDeprecated()).isTrue(); + } + + @Test + public void testOperationBecomesNotDeprecated() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/operation/becomes-not-deprecated"); + + assertThat(changedOperation).isNotNull(); + assertThat(changedOperation.isDeprecated()).isTrue(); } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java index ce233ed8b..40f0cba74 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java @@ -1,15 +1,277 @@ package org.openapitools.openapidiff.core; +import static io.swagger.v3.oas.models.PathItem.HttpMethod.GET; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.openapitools.openapidiff.core.ChangesResolver.getChangedOperation; +import static org.openapitools.openapidiff.core.ChangesResolver.getChangedParameter; +import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals; import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiChangedEndpoints; +import io.swagger.v3.oas.models.parameters.Parameter; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.model.ChangedOperation; +import org.openapitools.openapidiff.core.model.ChangedParameter; +import org.openapitools.openapidiff.core.model.ChangedParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ParameterDiffTest { - private final String OPENAPI_DOC1 = "parameters_diff_1.yaml"; - private final String OPENAPI_DOC2 = "parameters_diff_2.yaml"; + + private final String OPENAPI_DOC1 = "parameterDiff/parameter_diff_1.yaml"; + private final String OPENAPI_DOC2 = "parameterDiff/parameter_diff_2.yaml"; + private final String OVERLOADED_PARAMETERS = "parameterDiff/parameters_overloading.yaml"; + private final String DUPLICATED_PARAMETER_TYPES = "parameterDiff/parameters_overloading_2.yaml"; + + @Test + public void testAddParameter() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/parameter/added"); + + assertThat(changedOperation).isNotNull(); + List<Parameter> increasedParams = + Optional.ofNullable(changedOperation.getParameters()) + .map(ChangedParameters::getIncreased) + .orElse(Collections.emptyList()); + assertThat(increasedParams.size()).isEqualTo(1); + assertThat(increasedParams.get(0).getName()).isEqualTo("param"); + } + + @Test + public void testRemoveParameter() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedOperation changedOperation = + getChangedOperation(changedOpenApi, GET, "/parameter/removed"); + + assertThat(changedOperation).isNotNull(); + List<Parameter> missingParams = + Optional.ofNullable(changedOperation.getParameters()) + .map(ChangedParameters::getMissing) + .orElse(Collections.emptyList()); + assertThat(missingParams.size()).isEqualTo(1); + assertThat(missingParams.get(0).getName()).isEqualTo("param"); + } + + @Test + public void testParameterBecomesDeprecated() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedParameter changedParameter = + getChangedParameter(changedOpenApi, GET, "/parameter/becomes-deprecated", "X-Header"); + + assertThat(changedParameter).isNotNull(); + assertThat(changedParameter.isDeprecated()).isTrue(); + } + + @Test + public void testParameterBecomesNotDeprecated() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedParameter changedParameter = + getChangedParameter(changedOpenApi, GET, "/parameter/becomes-not-deprecated", "X-Header"); + + assertThat(changedParameter).isNotNull(); + assertThat(changedParameter.isDeprecated()).isTrue(); + } + + @Test + public void testParameterRequiredChanged() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedParameter changedParameter = + getChangedParameter(changedOpenApi, GET, "/parameter/required", "param"); + + assertThat(changedParameter).isNotNull(); + assertThat(changedParameter.isChangeRequired()).isTrue(); + } + + @Test + public void testParameterDescriptionChanged() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedParameter changedParameter = + getChangedParameter(changedOpenApi, GET, "/parameter/description", "param"); + + assertThat(changedParameter).isNotNull(); + assertThat(changedParameter.getDescription().getRight()).isEqualTo("changed"); + } + + @Test + public void testParameterExplodeChanged() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedParameter changedParameter = + getChangedParameter(changedOpenApi, GET, "/parameter/explode", "param"); + + assertThat(changedParameter).isNotNull(); + assertThat(changedParameter.isChangeExplode()).isTrue(); + } + + @Test + public void testParameterStyleChanged() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedParameter changedParameter = + getChangedParameter(changedOpenApi, GET, "/parameter/style", "param"); + + assertThat(changedParameter).isNotNull(); + assertThat(changedParameter.isChangeStyle()).isTrue(); + } + + @Test + void testDiffWithOverloadedParameterTypes() { + assertDoesNotThrow( + () -> OpenApiCompare.fromLocations(OVERLOADED_PARAMETERS, OVERLOADED_PARAMETERS)); + assertOpenApiAreEquals(OVERLOADED_PARAMETERS, OVERLOADED_PARAMETERS); + } + + @Test + void testDiffWithDuplicatedParameterTypes() { + assertThrows( + IllegalArgumentException.class, + () -> OpenApiCompare.fromLocations(DUPLICATED_PARAMETER_TYPES, DUPLICATED_PARAMETER_TYPES), + "Two path items have the same signature: /projects/{}"); + } + + @Test + public void issue458MaximumDecreased() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_2.yaml"); + } + + @Test + public void issue458MaximumIncreased() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_3.yaml"); + } + + @Test + public void issue458MinimumDecreased() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_4.yaml"); + } + + @Test + public void issue458MinimumIncreased() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_5.yaml"); + } + + @Test + public void issue458IntegerFormatChanged() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_6.yaml"); + } + + @Test + public void issue458ExclusiveMinimumChanged() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_7.yaml"); + } + + @Test + public void issue458ExclusiveMaximumChanged() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_8.yaml"); + } + + @Test + public void issue458ExclusiveMinimumRemoved() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_9.yaml"); + } + + @Test + public void issue458ExclusiveMaximumRemoved() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_1.yaml", + "parameterDiff/issue-458-integer-limits_10.yaml"); + } + + @Test + public void issue458ExclusiveMaximumTrueToFalse() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_11.yaml", + "parameterDiff/issue-458-integer-limits_12.yaml"); + } + + @Test + public void issue458ExclusiveMinimumTrueToFalse() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_11.yaml", + "parameterDiff/issue-458-integer-limits_13.yaml"); + } + + @Test + public void issue458ExclusiveMaximumTrueRemoved() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_11.yaml", + "parameterDiff/issue-458-integer-limits_12.yaml"); + } + + @Test + public void issue458ExclusiveMinimumTrueRemoved() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-458-integer-limits_11.yaml", + "parameterDiff/issue-458-integer-limits_13.yaml"); + } + + @Test + public void issue488RenameParameterAddAndRemoveParameterReturnFalse() { + assertOpenApiChangedEndpoints( + "parameterDiff/issue-488-1.json", "parameterDiff/issue-488-2.json"); + } + + final String TEST_MSG_1 = + "Testing: \n" + + "1. Same path but different pathParameters\n" + + "2. different parameters in the parameters: section\n" + + "3. Parameters have different schema\n" + + "eg:\n" + + "old path -- students/{id}\n" + + "old schema -- id: integer\n" + + "new path -- students/{username}\n" + + "new schema -- username: string"; + + @Test + @DisplayName( + "Same Path, different PathParams, Params in the `Parameters`: match pathParam, Different Schema") + public void pathSamePathParamsDiffParamSameAsInPathButSchemaDiff() { + final Logger logger = LoggerFactory.getLogger(ParameterDiffTest.class); + logger.info(TEST_MSG_1); + String OPENAPI_DOC1 = "parameterDiff/path_parameter_diff_param_schema_diff_old.yaml"; + String OPENAPI_DOC2 = "parameterDiff/path_parameter_diff_param_schema_diff_new.yaml"; + ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + Assertions.assertTrue(diff.isDifferent()); + Assertions.assertFalse(diff.isCompatible()); + } + + final String TEST_MSG_2 = + "Testing: \n" + + "1. Same path but different pathParameters\n" + + "2. different parameters in the parameters: section\n" + + "3. Parameters have same schema\n"; @Test - public void testDiffDifferent() { - assertOpenApiChangedEndpoints(OPENAPI_DOC1, OPENAPI_DOC2); + @DisplayName( + "Same Path, different PathParams, Params in the `Parameters`: match pathParam, same Schema") + public void pathSamePathParamsDiffParamNameDiffSchemaSame() { + final Logger logger = LoggerFactory.getLogger(ParameterDiffTest.class); + logger.info(TEST_MSG_2); + String OPENAPI_DOC1 = "parameterDiff/path_parameter_diff_param_name_diff_old.yaml"; + String OPENAPI_DOC2 = "parameterDiff/path_parameter_diff_param_name_diff_new.yaml"; + ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + Assertions.assertFalse(diff.isDifferent()); + Assertions.assertTrue(diff.isCompatible()); } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java index 1c8168c41..858ef520e 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java @@ -13,6 +13,8 @@ public class PathDiffTest { private final String OPENAPI_PATH2 = "path_2.yaml"; private final String OPENAPI_PATH3 = "path_3.yaml"; private final String OPENAPI_PATH4 = "path_4.yaml"; + private final String OPENAPI_PATH5 = "path_5.yaml"; + private final String OPENAPI_PATH6 = "path_6.yaml"; @Test public void testEqual() { @@ -35,4 +37,13 @@ public void testSameTemplateDifferentMethods() { assertThat(endpoint.getOperation().getOperationId()).isEqualTo("deletePet")); assertThat(changedOpenApi.isCompatible()).isTrue(); } + + @Test + public void testDiffWithSimilarBeginningPaths() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_PATH5, OPENAPI_PATH6); + ChangedOpenApi diff = + OpenApiCompare.fromSpecifications( + changedOpenApi.getOldSpecOpenApi(), changedOpenApi.getNewSpecOpenApi()); + assertThat(diff.getChangedOperations()).isEmpty(); + } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java index a4f0cabb0..725c7e0e9 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java @@ -3,6 +3,8 @@ import static org.assertj.core.api.Assertions.assertThat; import io.swagger.v3.oas.models.media.Content; +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; import java.util.Map; import org.junit.jupiter.api.Test; import org.openapitools.openapidiff.core.model.ChangedOpenApi; @@ -10,6 +12,7 @@ import org.openapitools.openapidiff.core.output.ConsoleRender; import org.openapitools.openapidiff.core.output.HtmlRender; import org.openapitools.openapidiff.core.output.MarkdownRender; +import org.openapitools.openapidiff.core.output.Render; public class ResponseAddedContentSchemaTest { @@ -39,8 +42,22 @@ public void testDiffDifferent() { public void testDiffCanBeRendered() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); - assertThat(new ConsoleRender().render(changedOpenApi)).isNotBlank(); - assertThat(new HtmlRender().render(changedOpenApi)).isNotBlank(); - assertThat(new MarkdownRender().render(changedOpenApi)).isNotBlank(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + Render render = new ConsoleRender(); + render.render(changedOpenApi, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + + outputStream = new ByteArrayOutputStream(); + outputStreamWriter = new OutputStreamWriter(outputStream); + render = new HtmlRender(); + render.render(changedOpenApi, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + + outputStream = new ByteArrayOutputStream(); + outputStreamWriter = new OutputStreamWriter(outputStream); + render = new MarkdownRender(); + render.render(changedOpenApi, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java index 05e4ecfe3..c388e9692 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java @@ -1,34 +1,86 @@ package org.openapitools.openapidiff.core; +import static io.swagger.v3.oas.models.PathItem.HttpMethod.GET; import static org.assertj.core.api.Assertions.assertThat; +import static org.openapitools.openapidiff.core.ChangesResolver.getChangedResponseHeaders; import java.util.Map; import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.ChangedHeader; import org.openapitools.openapidiff.core.model.ChangedHeaders; import org.openapitools.openapidiff.core.model.ChangedOpenApi; -import org.openapitools.openapidiff.core.model.ChangedResponse; public class ResponseHeaderDiffTest { - private final String OPENAPI_DOC1 = "header_1.yaml"; - private final String OPENAPI_DOC2 = "header_2.yaml"; + private final String OPENAPI_DOC1 = "responseHeaderDiff/response_header_1.yaml"; + private final String OPENAPI_DOC2 = "responseHeaderDiff/response_header_2.yaml"; @Test - public void testDiffDifferent() { + public void testResponseHeadersDescriptionChanges() { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedHeaders changedResponseHeaders = + getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/description", "200"); - assertThat(changedOpenApi.getNewEndpoints()).isEmpty(); - assertThat(changedOpenApi.getMissingEndpoints()).isEmpty(); - assertThat(changedOpenApi.getChangedOperations()).isNotEmpty(); - - Map<String, ChangedResponse> changedResponses = - changedOpenApi.getChangedOperations().get(0).getApiResponses().getChanged(); - assertThat(changedResponses).isNotEmpty(); - assertThat(changedResponses).containsKey("200"); - ChangedHeaders changedHeaders = changedResponses.get("200").getHeaders(); - assertThat(changedHeaders.isDifferent()).isTrue(); - assertThat(changedHeaders.getChanged()).hasSize(1); - assertThat(changedHeaders.getIncreased()).hasSize(1); - assertThat(changedHeaders.getMissing()).hasSize(1); + assertThat(changedResponseHeaders).isNotNull(); + Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged(); + + assertThat(changedHeaders).containsKey("x-header-description-changed"); + assertThat(changedHeaders.get("x-header-description-changed").getDescription()).isNotNull(); + assertThat(changedHeaders.get("x-header-description-changed").getDescription().getLeft()) + .isEqualTo("old description"); + assertThat(changedHeaders.get("x-header-description-changed").getDescription().getRight()) + .isEqualTo("new description"); + + assertThat(changedHeaders).containsKey("x-header-description-added"); + assertThat(changedHeaders.get("x-header-description-added").getDescription()).isNotNull(); + assertThat(changedHeaders.get("x-header-description-added").getDescription().getLeft()) + .isNull(); + assertThat(changedHeaders.get("x-header-description-added").getDescription().getRight()) + .isEqualTo("added description"); + + assertThat(changedHeaders).containsKey("x-header-description-removed"); + assertThat(changedHeaders.get("x-header-description-removed").getDescription()).isNotNull(); + assertThat(changedHeaders.get("x-header-description-removed").getDescription().getLeft()) + .isEqualTo("old description"); + assertThat(changedHeaders.get("x-header-description-removed").getDescription().getRight()) + .isNull(); + } + + @Test + public void testResponseHeadersRequiredChanges() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedHeaders changedResponseHeaders = + getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/required", "200"); + + assertThat(changedResponseHeaders).isNotNull(); + Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged(); + + assertThat(changedHeaders).containsKey("x-header-required-changed"); + } + + @Test + public void testResponseHeadersExplodeChanges() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedHeaders changedResponseHeaders = + getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/explode", "200"); + + assertThat(changedResponseHeaders).isNotNull(); + Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged(); + + assertThat(changedHeaders).containsKey("x-header-explode-changed"); + } + + @Test // issue #485 + public void testResponseHeadersDeprecatedChanges() { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2); + ChangedHeaders changedResponseHeaders = + getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/deprecated", "200"); + + assertThat(changedResponseHeaders).isNotNull(); + Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged(); + assertThat(changedHeaders).containsKey("x-header-becomes-deprecated"); + assertThat(changedHeaders.get("x-header-becomes-deprecated").isDeprecated()).isTrue(); + assertThat(changedHeaders).containsKey("x-header-becomes-not-deprecated"); + assertThat(changedHeaders.get("x-header-becomes-not-deprecated").isDeprecated()).isTrue(); } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java new file mode 100644 index 000000000..17014e868 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java @@ -0,0 +1,276 @@ +package org.openapitools.openapidiff.core; + +import static io.swagger.v3.oas.models.PathItem.HttpMethod.POST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.openapitools.openapidiff.core.ChangesResolver.getRequestBodyChangedSchema; +import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.math.BigDecimal; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.model.ChangedSchema; +import org.openapitools.openapidiff.core.output.ConsoleRender; + +public class SchemaDiffTest { + + @Test // issue #717 + public void schemaPropertyDefaultChanged() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-props-defaults-handling-1.yaml", + "schemaDiff/schema-props-defaults-handling-2.yaml"); + + assertEquals(1, changedOpenApi.getChangedOperations().size()); + assertEquals(1, changedOpenApi.getChangedSchemas().size()); + ChangedSchema changedSchema = changedOpenApi.getChangedSchemas().get(0); + assertEquals(1, changedSchema.getChangedProperties().size()); + assertTrue(changedSchema.getChangedProperties().containsKey("field1")); + assertTrue(changedSchema.getChangedProperties().get("field1").isChangeDefault()); + } + + @Test + public void schemaPropertyTypeChanged() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-props-defaults-handling-1.yaml", + "schemaDiff/schema-props-defaults-handling-2.yaml"); + } + + @Test // issue #485 + public void schemaBecomesDeprecatedTest() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-deprecated-handling-1.yaml", + "schemaDiff/schema-deprecated-handling-2.yaml"); + + ChangedSchema requestBodySchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema-diff/deprecated/added", "application/json"); + assertNotNull(requestBodySchema); + assertTrue(requestBodySchema.isChangeDeprecated()); + } + + @Test // issue #485 + public void schemaBecomesNotDeprecatedTest() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-deprecated-handling-1.yaml", + "schemaDiff/schema-deprecated-handling-2.yaml"); + + ChangedSchema requestBodySchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema-diff/deprecated/removed", "application/json"); + assertNotNull(requestBodySchema); + assertTrue(requestBodySchema.isChangeDeprecated()); + } + + @Test // issue #256 + void booleanAdditionalPropertiesAreSupported() { + ChangedOpenApi diff = + OpenApiCompare.fromLocations( + "schemaDiff/schema-additional-properties-1.json", + "schemaDiff/schema-additional-properties-2.json"); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ConsoleRender render = new ConsoleRender(); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void addPropertyHandlingTest() { + assertOpenApiBackwardCompatible( + "schemaDiff/schema-add-property-1.yaml", "schemaDiff/schema-add-property-2.yaml", true); + } + + @Test // issue #537 + public void addPropertyInPutApiIsCompatible() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-add-property-put-1.yaml", + "schemaDiff/schema-add-property-put-2.yaml"); + + assertThat(changedOpenApi.isDifferent()).isTrue(); + assertThat(changedOpenApi.isCompatible()).isTrue(); + } + + @Test // issues #483 #463 + public void changeMultipleOfHandling() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-multiple-of-diff-1.yaml", + "schemaDiff/schema-multiple-of-diff-2.yaml"); + ChangedSchema changedSchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema/numeric/multiple-of", "application/json"); + + assertThat(changedSchema).isNotNull(); + Map<String, ChangedSchema> props = changedSchema.getChangedProperties(); + assertThat(props).isNotEmpty(); + + // Check changes in multipleOf + assertThat(props.get("field1").getMultipleOf().isIncompatible()).isTrue(); + assertThat(props.get("field1").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(10)); + assertThat(props.get("field1").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(20)); + + assertThat(props.get("field2").getMultipleOf().isIncompatible()).isTrue(); + assertThat(props.get("field2").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(0.01)); + assertThat(props.get("field2").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(0.1)); + + // Check addition of multipleOf + assertThat(props.get("field3").getMultipleOf().isIncompatible()).isTrue(); + assertThat(props.get("field3").getMultipleOf().getLeft()).isNull(); + assertThat(props.get("field3").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(10)); + + // Check deletion of multipleOf + assertThat(props.get("field4").getMultipleOf().isCompatible()).isTrue(); + assertThat(props.get("field4").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(10)); + assertThat(props.get("field4").getMultipleOf().getRight()).isNull(); + } + + @Test // issues #480 + public void changeMinMaxItemsHandling() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-min-max-items-diff-1.yaml", + "schemaDiff/schema-min-max-items-diff-2.yaml"); + ChangedSchema changedSchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema/array/items", "application/json"); + + assertThat(changedSchema).isNotNull(); + Map<String, ChangedSchema> props = changedSchema.getChangedProperties(); + assertThat(props).isNotEmpty(); + + // Check increasing of minItems + assertThat(props.get("field1").getMinItems().isIncompatible()).isTrue(); + assertThat(props.get("field1").getMinItems().getOldValue()).isEqualTo(1); + assertThat(props.get("field1").getMinItems().getNewValue()).isEqualTo(2); + + // Check decreasing of minItems + assertThat(props.get("field2").getMinItems().isCompatible()).isTrue(); + assertThat(props.get("field2").getMinItems().getOldValue()).isEqualTo(20); + assertThat(props.get("field2").getMinItems().getNewValue()).isEqualTo(10); + + // Check increasing of maxItems + assertThat(props.get("field3").getMaxItems().isCompatible()).isTrue(); + assertThat(props.get("field3").getMaxItems().getOldValue()).isEqualTo(90); + assertThat(props.get("field3").getMaxItems().getNewValue()).isEqualTo(100); + + // Check decreasing of maxItems + assertThat(props.get("field4").getMaxItems().isIncompatible()).isTrue(); + assertThat(props.get("field4").getMaxItems().getOldValue()).isEqualTo(100); + assertThat(props.get("field4").getMaxItems().getNewValue()).isEqualTo(90); + } + + @Test // issue #482 + public void changeNullableHandling() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-nullable-diff-1.yaml", "schemaDiff/schema-nullable-diff-2.yaml"); + ChangedSchema changedSchema = + getRequestBodyChangedSchema(changedOpenApi, POST, "/schema/nullable", "application/json"); + + assertThat(changedSchema).isNotNull(); + Map<String, ChangedSchema> props = changedSchema.getChangedProperties(); + assertThat(props).isNotEmpty(); + + // Check no changes in nullable + assertThat(props.get("field0")).isNull(); + + // Check changes in nullable + assertThat(props.get("field1").getNullable().isCompatible()).isTrue(); + assertThat(props.get("field1").getNullable().getLeft()).isTrue(); + assertThat(props.get("field1").getNullable().getRight()).isFalse(); + + // Check deletion of nullable + assertThat(props.get("field2").getNullable().isCompatible()).isTrue(); + assertThat(props.get("field2").getNullable().getLeft()).isTrue(); + assertThat(props.get("field2").getNullable().getRight()).isNull(); + + // Check addition of nullable + assertThat(props.get("field3").getNullable().isCompatible()).isTrue(); + assertThat(props.get("field3").getNullable().getLeft()).isNull(); + assertThat(props.get("field3").getNullable().getRight()).isTrue(); + } + + @Test // issue #478 + public void changeUniqueItemsHandling() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-uniqueItems-diff-1.yaml", + "schemaDiff/schema-uniqueItems-diff-2.yaml"); + ChangedSchema changedSchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema/uniqueItems", "application/json"); + + assertThat(changedSchema).isNotNull(); + Map<String, ChangedSchema> props = changedSchema.getChangedProperties(); + assertThat(props).isNotEmpty(); + + // Check no changes in uniqueItems + assertThat(props.get("field0")).isNull(); + + // Check changes true -> false + assertThat(props.get("field1").getUniqueItems().isCompatible()).isTrue(); + assertThat(props.get("field1").getUniqueItems().getLeft()).isTrue(); + assertThat(props.get("field1").getUniqueItems().getRight()).isFalse(); + + // Check changes false -> true + assertThat(props.get("field2").getUniqueItems().isIncompatible()).isTrue(); + assertThat(props.get("field2").getUniqueItems().getLeft()).isFalse(); + assertThat(props.get("field2").getUniqueItems().getRight()).isTrue(); + + // Check deletion of uniqueItems + assertThat(props.get("field3").getUniqueItems().isCompatible()).isTrue(); + assertThat(props.get("field3").getUniqueItems().getLeft()).isTrue(); + assertThat(props.get("field3").getUniqueItems().getRight()).isNull(); + + // Check addition of uniqueItems + assertThat(props.get("field4").getUniqueItems().isIncompatible()).isTrue(); + assertThat(props.get("field4").getUniqueItems().getLeft()).isNull(); + assertThat(props.get("field4").getUniqueItems().getRight()).isTrue(); + } + + @Test // issue #479 + public void changeMinMaxPropertiesHandling() { + ChangedOpenApi changedOpenApi = + OpenApiCompare.fromLocations( + "schemaDiff/schema-min-max-properties-diff-1.yaml", + "schemaDiff/schema-min-max-properties-diff-2.yaml"); + ChangedSchema changedSchema = + getRequestBodyChangedSchema( + changedOpenApi, POST, "/schema/object/min-max-properties", "application/json"); + + assertThat(changedSchema).isNotNull(); + Map<String, ChangedSchema> props = changedSchema.getChangedProperties(); + assertThat(props).isNotEmpty(); + + // Check increasing of minProperties + assertThat(props.get("field1").getMinProperties().isIncompatible()).isTrue(); + assertThat(props.get("field1").getMinProperties().getOldValue()).isEqualTo(1); + assertThat(props.get("field1").getMinProperties().getNewValue()).isEqualTo(10); + + // Check decreasing of minProperties + assertThat(props.get("field2").getMinProperties().isCompatible()).isTrue(); + assertThat(props.get("field2").getMinProperties().getOldValue()).isEqualTo(10); + assertThat(props.get("field2").getMinProperties().getNewValue()).isEqualTo(1); + + // Check increasing of maxProperties + assertThat(props.get("field3").getMaxProperties().isCompatible()).isTrue(); + assertThat(props.get("field3").getMaxProperties().getOldValue()).isEqualTo(10); + assertThat(props.get("field3").getMaxProperties().getNewValue()).isEqualTo(100); + + // Check decreasing of maxProperties + assertThat(props.get("field4").getMaxProperties().isIncompatible()).isTrue(); + assertThat(props.get("field4").getMaxProperties().getOldValue()).isEqualTo(100); + assertThat(props.get("field4").getMaxProperties().getNewValue()).isEqualTo(10); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java b/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java index 1180f2886..597bf830f 100644 --- a/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java +++ b/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java @@ -3,7 +3,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.slf4j.LoggerFactory.getLogger; +import java.util.List; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.model.ChangedOperation; +import org.openapitools.openapidiff.core.model.DiffResult; import org.slf4j.Logger; public class TestUtils { @@ -25,6 +30,38 @@ public static void assertOpenApiChangedEndpoints(String oldSpec, String newSpec) assertThat(changedOpenApi.getChangedOperations()).isNotEmpty(); } + public static void assertSpecUnchanged(String oldSpec, String newSpec) { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec); + LOG.info("Result: {}", changedOpenApi.isChanged().getValue()); + assertThat(changedOpenApi.isUnchanged()).isTrue(); + } + + public static void assertSpecChangedButCompatible(String oldSpec, String newSpec) { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec); + DiffResult diffResult = changedOpenApi.isChanged(); + LOG.info("Result: {}", diffResult.getValue()); + assertThat(diffResult.isDifferent()).isTrue(); + assertThat(diffResult.isCompatible()).isTrue(); + } + + public static void assertSpecIncompatible( + String oldSpec, String newSpec, BackwardIncompatibleProp prop) { + OpenApiDiffOptions.Builder builder = OpenApiDiffOptions.builder(); + // Expect incompatible when BackwardIncompatibleProp enabled + builder.configProperty(prop.getPropertyName(), "true"); + OpenApiDiffOptions optsIncompat = builder.build(); + ChangedOpenApi apiIncompat = OpenApiCompare.fromLocations(oldSpec, newSpec, null, optsIncompat); + LOG.info("Result: {}", apiIncompat.isChanged().getValue()); + assertThat(apiIncompat.isIncompatible()).isTrue(); + // Expect changed but compatible when BackwardIncompatibleProp disabled + builder.configProperty(prop.getPropertyName(), "false"); + OpenApiDiffOptions optsCompat = builder.build(); + ChangedOpenApi apiCompat = OpenApiCompare.fromLocations(oldSpec, newSpec, null, optsCompat); + LOG.info("Result: {}", apiCompat.isChanged().getValue()); + assertThat(apiCompat.isDifferent()).isTrue(); + assertThat(apiCompat.isCompatible()).isTrue(); + } + public static void assertOpenApiBackwardCompatible( String oldSpec, String newSpec, boolean isDiff) { ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec); @@ -37,4 +74,9 @@ public static void assertOpenApiBackwardIncompatible(String oldSpec, String newS LOG.info("Result: {}", changedOpenApi.isChanged().getValue()); assertThat(changedOpenApi.isIncompatible()).isTrue(); } + + public static List<ChangedOperation> getOpenApiChangedOperations(String oldSpec, String newSpec) { + ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec); + return changedOpenApi.getChangedOperations(); + } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ApiResponseBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ApiResponseBCTest.java new file mode 100644 index 000000000..045a16e48 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ApiResponseBCTest.java @@ -0,0 +1,29 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_RESPONSES_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class ApiResponseBCTest { + private final String BASE = "bc_response_apiresponse_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_response_apiresponse_changed_but_compatible.yaml"); + } + + @Test + public void decreased() { + BackwardIncompatibleProp prop = RESPONSE_RESPONSES_DECREASED; + assertSpecIncompatible(BASE, "bc_response_apiresponse_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ContentBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ContentBCTest.java new file mode 100644 index 000000000..8bd95764e --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ContentBCTest.java @@ -0,0 +1,36 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_CONTENT_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_CONTENT_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class ContentBCTest { + private final String BASE = "bc_content_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_content_changed_but_compatible.yaml"); + } + + @Test + public void requestDecreased() { + BackwardIncompatibleProp prop = REQUEST_CONTENT_DECREASED; + assertSpecIncompatible(BASE, "bc_request_content_decreased.yaml", prop); + } + + @Test + public void responseDecreased() { + BackwardIncompatibleProp prop = RESPONSE_CONTENT_DECREASED; + assertSpecIncompatible(BASE, "bc_response_content_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/EnumBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/EnumBCTest.java new file mode 100644 index 000000000..d5a7921a4 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/EnumBCTest.java @@ -0,0 +1,36 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ENUM_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ENUM_INCREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class EnumBCTest { + private final String BASE = "bc_enum_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_enum_changed_but_compatible.yaml"); + } + + @Test + public void requestDecreased() { + BackwardIncompatibleProp prop = REQUEST_ENUM_DECREASED; + assertSpecIncompatible(BASE, "bc_request_enum_decreased.yaml", prop); + } + + @Test + public void responseIncreased() { + BackwardIncompatibleProp prop = RESPONSE_ENUM_INCREASED; + assertSpecIncompatible(BASE, "bc_response_enum_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeaderBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeaderBCTest.java new file mode 100644 index 000000000..bf36d7772 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeaderBCTest.java @@ -0,0 +1,44 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_EXPLODE_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_INCREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class HeaderBCTest { + private final String BASE = "bc_response_header_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_response_header_changed_but_compatible.yaml"); + } + + @Test + public void responseExplodeChanged() { + BackwardIncompatibleProp prop = RESPONSE_HEADER_EXPLODE_CHANGED; + assertSpecIncompatible(BASE, "bc_response_header_explode_changed.yaml", prop); + } + + @Test + public void responseRequiredDecreased() { + BackwardIncompatibleProp prop = RESPONSE_HEADER_REQUIRED_DECREASED; + assertSpecIncompatible(BASE, "bc_response_header_required_decreased.yaml", prop); + } + + @Test + public void responseRequiredIncreased() { + // TODO: Document why desired or remove support (test added to avoid unintentional regression) + BackwardIncompatibleProp prop = RESPONSE_HEADER_REQUIRED_INCREASED; + assertSpecIncompatible(BASE, "bc_response_header_required_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeadersBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeadersBCTest.java new file mode 100644 index 000000000..e08b4b8c8 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeadersBCTest.java @@ -0,0 +1,29 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADERS_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class HeadersBCTest { + private final String BASE = "bc_response_headers_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_response_headers_changed_but_compatible.yaml"); + } + + @Test + public void responseDecreased() { + BackwardIncompatibleProp prop = RESPONSE_HEADERS_DECREASED; + assertSpecIncompatible(BASE, "bc_response_headers_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MaxLengthBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MaxLengthBCTest.java new file mode 100644 index 000000000..243ff892a --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MaxLengthBCTest.java @@ -0,0 +1,30 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_MAX_LENGTH_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_MAX_LENGTH_INCREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class MaxLengthBCTest { + private final String BASE = "bc_maxlength_base.yaml"; + + @Test + public void maxLengthUnchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void requestMaxLengthDecreased() { + BackwardIncompatibleProp prop = REQUEST_MAX_LENGTH_DECREASED; + assertSpecIncompatible(BASE, "bc_request_maxlength_decreased.yaml", prop); + } + + @Test + public void responseMaxLengthIncreased() { + BackwardIncompatibleProp prop = RESPONSE_MAX_LENGTH_INCREASED; + assertSpecIncompatible(BASE, "bc_response_maxlength_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/NumericRangeBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/NumericRangeBCTest.java new file mode 100644 index 000000000..a589c7ea0 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/NumericRangeBCTest.java @@ -0,0 +1,111 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_NUMERIC_RANGE_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_NUMERIC_RANGE_INCREASED; + +import org.junit.jupiter.api.Test; + +public class NumericRangeBCTest { + private final String BASE = "bc_numericrange_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_numericrange_changed_but_compatible.yaml"); + } + + @Test + public void requestExclusiveMaxCreated() { + assertIncompatibleRequest("bc_request_numericrange_exclusive_max_created.yaml"); + } + + @Test + public void requestExclusiveMaxSet() { + assertIncompatibleRequest("bc_request_numericrange_exclusive_max_set.yaml"); + } + + @Test + public void requestExclusiveMinCreated() { + assertIncompatibleRequest("bc_request_numericrange_exclusive_min_created.yaml"); + } + + @Test + public void requestExclusiveMinSet() { + assertIncompatibleRequest("bc_request_numericrange_exclusive_min_set.yaml"); + } + + @Test + public void requestMaxAdded() { + assertIncompatibleRequest("bc_request_numericrange_max_added.yaml"); + } + + @Test + public void requestMaxDecreased() { + assertIncompatibleRequest("bc_request_numericrange_max_decreased.yaml"); + } + + @Test + public void requestMinAdded() { + assertIncompatibleRequest("bc_request_numericrange_min_added.yaml"); + } + + @Test + public void requestMinIncreased() { + assertIncompatibleRequest("bc_request_numericrange_min_increased.yaml"); + } + + @Test + public void responseExclusiveMaxDeleted() { + assertIncompatibleResponse("bc_response_numericrange_exclusive_max_deleted.yaml"); + } + + @Test + public void responseExclusiveMaxUnset() { + assertIncompatibleResponse("bc_response_numericrange_exclusive_max_unset.yaml"); + } + + @Test + public void responseExclusiveMinDeleted() { + assertIncompatibleResponse("bc_response_numericrange_exclusive_min_deleted.yaml"); + } + + @Test + public void responseExclusiveMinUnset() { + assertIncompatibleResponse("bc_response_numericrange_exclusive_min_unset.yaml"); + } + + @Test + public void responseMaxDeleted() { + assertIncompatibleResponse("bc_response_numericrange_max_deleted.yaml"); + } + + @Test + public void responseMaxIncreased() { + assertIncompatibleResponse("bc_response_numericrange_max_increased.yaml"); + } + + @Test + public void responseMinDecreased() { + assertIncompatibleResponse("bc_response_numericrange_min_decreased.yaml"); + } + + @Test + public void responseMinDeleted() { + assertIncompatibleResponse("bc_response_numericrange_min_deleted.yaml"); + } + + private void assertIncompatibleRequest(String newSpec) { + assertSpecIncompatible(BASE, newSpec, REQUEST_NUMERIC_RANGE_DECREASED); + } + + private void assertIncompatibleResponse(String newSpec) { + assertSpecIncompatible(BASE, newSpec, RESPONSE_NUMERIC_RANGE_INCREASED); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OAuthFlowBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OAuthFlowBCTest.java new file mode 100644 index 000000000..476546504 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OAuthFlowBCTest.java @@ -0,0 +1,37 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class OAuthFlowBCTest { + private final String BASE = "bc_oauthflow_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void authorizationUrlChanged() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED; + assertSpecIncompatible(BASE, "bc_oauthflow_authorization_url_changed.yaml", prop); + } + + @Test + public void refreshUrlChanged() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED; + assertSpecIncompatible(BASE, "bc_oauthflow_refresh_url_changed.yaml", prop); + } + + @Test + public void tokenUrlChanged() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED; + assertSpecIncompatible(BASE, "bc_oauthflow_token_url_changed.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OneOfBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OneOfBCTest.java new file mode 100644 index 000000000..fb2c06291 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OneOfBCTest.java @@ -0,0 +1,36 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ONEOF_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ONEOF_INCREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class OneOfBCTest { + private final String BASE = "bc_oneof_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_oneof_changed_but_compatible.yaml"); + } + + @Test + public void requestDecreased() { + BackwardIncompatibleProp prop = REQUEST_ONEOF_DECREASED; + assertSpecIncompatible(BASE, "bc_request_oneof_decreased.yaml", prop); + } + + @Test + public void responseIncreased() { + BackwardIncompatibleProp prop = RESPONSE_ONEOF_INCREASED; + assertSpecIncompatible(BASE, "bc_response_oneof_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OpenApiBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OpenApiBCTest.java new file mode 100644 index 000000000..2d3e53065 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OpenApiBCTest.java @@ -0,0 +1,29 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class OpenApiBCTest { + private final String BASE = "bc_openapi_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_openapi_changed_but_compatible.yaml"); + } + + @Test + public void endpointsDecreased() { + BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED; + assertSpecIncompatible(BASE, "bc_openapi_endpoints_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OperationBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OperationBCTest.java new file mode 100644 index 000000000..c95d8c9d2 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OperationBCTest.java @@ -0,0 +1,20 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; + +import org.junit.jupiter.api.Test; + +public class OperationBCTest { + private final String BASE = "bc_operation_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_operation_changed_but_compatible.yaml"); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParameterBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParameterBCTest.java new file mode 100644 index 000000000..edcd40073 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParameterBCTest.java @@ -0,0 +1,50 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_REQUIRED_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAM_ALLOWEMPTY_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAM_EXPLODE_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAM_STYLE_CHANGED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class ParameterBCTest { + private final String BASE = "bc_request_param_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_request_param_changed_but_compatible.yaml"); + } + + @Test + public void allowEmptyValueDecreased() { + BackwardIncompatibleProp prop = REQUEST_PARAM_ALLOWEMPTY_DECREASED; + assertSpecIncompatible(BASE, "bc_request_param_allowemptyvalue_decreased.yaml", prop); + } + + @Test + public void explodeChanged() { + BackwardIncompatibleProp prop = REQUEST_PARAM_EXPLODE_CHANGED; + assertSpecIncompatible(BASE, "bc_request_param_explode_changed.yaml", prop); + } + + @Test + public void requiredIncreased() { + BackwardIncompatibleProp prop = REQUEST_PARAMS_REQUIRED_INCREASED; + assertSpecIncompatible(BASE, "bc_request_param_required_increased.yaml", prop); + } + + @Test + public void styleChanged() { + BackwardIncompatibleProp prop = REQUEST_PARAM_STYLE_CHANGED; + assertSpecIncompatible(BASE, "bc_request_param_style_changed.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParametersBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParametersBCTest.java new file mode 100644 index 000000000..5f2755bbc --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParametersBCTest.java @@ -0,0 +1,36 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_REQUIRED_INCREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class ParametersBCTest { + private final String BASE = "bc_request_params_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_request_params_changed_but_compatible.yaml"); + } + + @Test + public void decreased() { + BackwardIncompatibleProp prop = REQUEST_PARAMS_DECREASED; + assertSpecIncompatible(BASE, "bc_request_params_decreased.yaml", prop); + } + + @Test + public void requiredIncreased() { + BackwardIncompatibleProp prop = REQUEST_PARAMS_REQUIRED_INCREASED; + assertSpecIncompatible(BASE, "bc_request_params_required_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathBCTest.java new file mode 100644 index 000000000..c1b2a60f1 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathBCTest.java @@ -0,0 +1,29 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class PathBCTest { + private final String BASE = "bc_path_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_path_changed_but_compatible.yaml"); + } + + @Test + public void opsDecreased() { + BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED; + assertSpecIncompatible(BASE, "bc_path_ops_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathsBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathsBCTest.java new file mode 100644 index 000000000..d865ee44a --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathsBCTest.java @@ -0,0 +1,29 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class PathsBCTest { + private final String BASE = "bc_paths_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_paths_changed_but_compatible.yaml"); + } + + @Test + public void decreased() { + BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED; + assertSpecIncompatible(BASE, "bc_paths_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ReadOnlyBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ReadOnlyBCTest.java new file mode 100644 index 000000000..02d2fa9a0 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ReadOnlyBCTest.java @@ -0,0 +1,37 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_REQUIRED_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class ReadOnlyBCTest { + private final String BASE = "bc_readonly_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_readonly_changed_but_compatible.yaml"); + } + + @Test + public void requestReadOnlyIncreased() { + BackwardIncompatibleProp prop = REQUEST_READONLY_INCREASED; + assertSpecIncompatible(BASE, "bc_request_readonly_increased.yaml", prop); + } + + @Test + public void requestReadOnlyRequiredDecreased() { + // Incompatible because a prev RO prop (invalid) is now not RO and required + BackwardIncompatibleProp prop = REQUEST_READONLY_REQUIRED_DECREASED; + assertSpecIncompatible(BASE, "bc_request_readonly_required_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequestBodyBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequestBodyBCTest.java new file mode 100644 index 000000000..5b1000702 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequestBodyBCTest.java @@ -0,0 +1,23 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class RequestBodyBCTest { + private final String BASE = "bc_request_body_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void requiredChanged() { + BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED; + assertSpecIncompatible(BASE, "bc_request_body_required_changed.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequiredBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequiredBCTest.java new file mode 100644 index 000000000..c7cfadb0a --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequiredBCTest.java @@ -0,0 +1,37 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_REQUIRED_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class RequiredBCTest { + + private final String BASE = "bc_required_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_required_changed_but_compatible.yaml"); + } + + @Test + public void requestRequiredIncreased() { + BackwardIncompatibleProp prop = REQUEST_REQUIRED_INCREASED; + assertSpecIncompatible(BASE, "bc_request_required_increased.yaml", prop); + } + + @Test + public void responseRequiredDecreased() { + BackwardIncompatibleProp prop = RESPONSE_REQUIRED_DECREASED; + assertSpecIncompatible(BASE, "bc_response_required_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SchemaBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SchemaBCTest.java new file mode 100644 index 000000000..781b48f2a --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SchemaBCTest.java @@ -0,0 +1,72 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_DISCRIMINATOR_CHANGED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_TYPE_CHANGED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class SchemaBCTest { + private final String BASE = "bc_schema_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_schema_changed_but_compatible.yaml"); + } + + @Test + public void discriminatorChanged() { + BackwardIncompatibleProp prop = SCHEMA_DISCRIMINATOR_CHANGED; + assertSpecIncompatible(BASE, "bc_schema_discriminator_changed.yaml", prop); + } + + @Test + public void requestFormatDecreased() { + assertOpenApiBackwardIncompatible(BASE, "bc_request_schema_format_decreased.yaml"); + } + + @Test + public void requestFormatIncreased() { + // TODO: Document why desired or remove support (test added to avoid unintentional regression) + assertOpenApiBackwardIncompatible(BASE, "bc_request_schema_format_increased.yaml"); + } + + @Test + public void requestPropsPutIncreased() { + // See https://github.com/OpenAPITools/openapi-diff/issues/537 + assertSpecChangedButCompatible(BASE, "bc_request_schema_props_put_increased.yaml"); + } + + @Test + public void responseFormatDecreased() { + // TODO: Document why desired or remove support (test added to avoid unintentional regression) + assertOpenApiBackwardIncompatible(BASE, "bc_response_schema_format_decreased.yaml"); + } + + @Test + public void responseFormatIncreased() { + assertOpenApiBackwardIncompatible(BASE, "bc_response_schema_format_increased.yaml"); + } + + @Test + public void responsePropsRequiredDecreased() { + BackwardIncompatibleProp prop = RESPONSE_REQUIRED_DECREASED; + assertSpecIncompatible(BASE, "bc_response_schema_props_required_decreased.yaml", prop); + } + + @Test + public void typeChanged() { + BackwardIncompatibleProp prop = SCHEMA_TYPE_CHANGED; + assertSpecIncompatible(BASE, "bc_schema_type_changed.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementBCTest.java new file mode 100644 index 000000000..73a3ca227 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementBCTest.java @@ -0,0 +1,27 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.*; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENT_SCHEMES_INCREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class SecurityRequirementBCTest { + private final String BASE = "bc_security_requirement_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_security_requirement_changed_but_compatible.yaml"); + } + + @Test + public void schemesIncreased() { + BackwardIncompatibleProp prop = SECURITY_REQUIREMENT_SCHEMES_INCREASED; + assertSpecIncompatible(BASE, "bc_security_requirement_schemes_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementsBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementsBCTest.java new file mode 100644 index 000000000..931b35bce --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementsBCTest.java @@ -0,0 +1,39 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.*; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENTS_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class SecurityRequirementsBCTest { + private final String BASE = "bc_security_requirements_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_security_requirements_changed_but_compatible.yaml"); + } + + // TODO: Dropping *all* security requirements should be compatible. Refactor or document + // reasoning. Context: OAS spec is clear that only one of the security requirement objects + // need to be satisfied so it makes sense why dropping one could break a client that may + // not yet support one of the remaining referenced security schemes. But dropping all + // requirements should be compatible. + @Test + public void decreased() { + BackwardIncompatibleProp prop = SECURITY_REQUIREMENTS_DECREASED; + assertSpecIncompatible(BASE, "bc_security_requirements_decreased.yaml", prop); + } + + // TODO: A missing incompatible check seems to be if requirements increase from zero to 1 or more + + @Test + public void schemeTypeChanged() { + assertOpenApiBackwardIncompatible(BASE, "bc_security_requirements_scheme_type_changed.yaml"); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecuritySchemeBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecuritySchemeBCTest.java new file mode 100644 index 000000000..d89d824d5 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecuritySchemeBCTest.java @@ -0,0 +1,55 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.*; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.*; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class SecuritySchemeBCTest { + private final String BASE = "bc_security_scheme_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_security_scheme_changed_but_compatible.yaml"); + } + + @Test + public void bearerFormatChanged() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_BEARER_FORMAT_CHANGED; + assertSpecIncompatible(BASE, "bc_security_scheme_bearer_format_changed.yaml", prop); + } + + @Test + public void inChanged() { + assertOpenApiBackwardIncompatible(BASE, "bc_security_scheme_in_changed.yaml"); + } + + @Test + public void openIdConnectUrlChanged() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_OPENIDCONNECT_URL_CHANGED; + assertSpecIncompatible(BASE, "bc_security_scheme_open_id_connect_url_changed.yaml", prop); + } + + @Test + public void schemeChanged() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_SCHEME_CHANGED; + assertSpecIncompatible(BASE, "bc_security_scheme_scheme_changed.yaml", prop); + } + + @Test + public void typeChanged() { + assertOpenApiBackwardIncompatible(BASE, "bc_security_scheme_type_changed.yaml"); + } + + @Test + public void scopesIncreased() { + BackwardIncompatibleProp prop = SECURITY_SCHEME_SCOPES_INCREASED; + assertSpecIncompatible(BASE, "bc_security_scheme_scopes_increased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/WriteOnlyBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/WriteOnlyBCTest.java new file mode 100644 index 000000000..a12853c82 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/WriteOnlyBCTest.java @@ -0,0 +1,37 @@ +package org.openapitools.openapidiff.core.backcompat; + +import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible; +import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_INCREASED; +import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_REQUIRED_DECREASED; + +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp; + +public class WriteOnlyBCTest { + private final String BASE = "bc_writeonly_base.yaml"; + + @Test + public void unchanged() { + assertSpecUnchanged(BASE, BASE); + } + + @Test + public void changedButCompatible() { + assertSpecChangedButCompatible(BASE, "bc_writeonly_changed_but_compatible.yaml"); + } + + @Test + public void responseWriteOnlyIncreased() { + BackwardIncompatibleProp prop = RESPONSE_WRITEONLY_INCREASED; + assertSpecIncompatible(BASE, "bc_response_writeonly_increased.yaml", prop); + } + + @Test + public void responseWriteOnlyRequiredDecreased() { + BackwardIncompatibleProp prop = RESPONSE_WRITEONLY_REQUIRED_DECREASED; + // TODO: Document why desired or remove support (test added to avoid unintentional regression) + assertSpecIncompatible(BASE, "bc_response_writeonly_required_decreased.yaml", prop); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/compare/ApiResponseDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/compare/ApiResponseDiffTest.java deleted file mode 100644 index 97b8ab094..000000000 --- a/core/src/test/java/org/openapitools/openapidiff/core/compare/ApiResponseDiffTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.openapitools.openapidiff.core.compare; - -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible; -import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class ApiResponseDiffTest extends Assertions { - /** - * * This is a regression test - when no responses were set, we would get an exception since the - * OpenAPI object has a `null` ApiResponses field. - */ - @Test - public void testNoResponseInPrevious() { - // The previous API had no response, so adding a response shape is still compatible. - assertOpenApiBackwardCompatible( - "backwardCompatibility/apiResponse_diff_1.yaml", - "backwardCompatibility/apiResponse_diff_2.yaml", - true); - - // Removing the response shape is backwards incompatible. - assertOpenApiBackwardIncompatible( - "backwardCompatibility/apiResponse_diff_2.yaml", - "backwardCompatibility/apiResponse_diff_1.yaml"); - } -} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/AsciidocRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/AsciidocRenderTest.java new file mode 100644 index 000000000..228623a2c --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/AsciidocRenderTest.java @@ -0,0 +1,96 @@ +package org.openapitools.openapidiff.core.output; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class AsciidocRenderTest { + @Test + public void renderDoesNotFailWhenPropertyHasBeenRemoved() { + AsciidocRender render = new AsciidocRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderDoesNotCauseStackOverflowWithRecursiveDefinitions() { + AsciidocRender render = new AsciidocRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = OpenApiCompare.fromLocations("recursive_old.yaml", "recursive_new.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderDoesNotFailWhenHTTPStatusCodeIsRange() { + AsciidocRender render = new AsciidocRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void validateAsciiDocChangeFile() { + AsciidocRender render = new AsciidocRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()) + .isEqualTo( + "= TITLE (v 1.0.0)\n" + + ":reproducible:\n" + + ":sectlinks:\n" + + ":toc:\n" + + "\n" + + "== What's Changed\n" + + "=== GET /\n" + + "* Return Type:\n" + + "** Changed default \n" + + "** Media types:\n" + + "*** Changed application/json\n" + + "*** Schema:\n" + + "Backward compatible\n" + + "\n" + + "\n" + + "NOTE: API changes are backward compatible\n"); + } + + @Test + public void validateAsciiDocRangeStatus() { + AsciidocRender render = new AsciidocRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()) + .isEqualTo( + "= PROJECTS API (v 1.0.0)\n" + + ":reproducible:\n" + + ":sectlinks:\n" + + ":toc:\n" + + "\n" + + "== What's Changed\n" + + "=== GET /pet/\n" + + "* Return Type:\n" + + "** Add 4XX \n" + + "** Deleted 405 Method Not Allowed\n" + + "\n" + + "\n" + + "WARNING: API changes broke backward compatibility\n"); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/ConsoleRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/ConsoleRenderTest.java new file mode 100644 index 000000000..675633fcd --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/ConsoleRenderTest.java @@ -0,0 +1,79 @@ +package org.openapitools.openapidiff.core.output; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class ConsoleRenderTest { + @Test + public void renderDoesNotFailWhenPropertyHasBeenRemoved() { + ConsoleRender render = new ConsoleRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderDoesNotFailWhenHTTPStatusCodeIsRange() { + ConsoleRender render = new ConsoleRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderShowsWhatsDeletedSectionWhenEndpointIsDeleted() { + ConsoleRender render = new ConsoleRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("delete_endpoint_1.yaml", "delete_endpoint_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).contains("What's Deleted"); + } + + @Test + public void renderShowsWhatsNewSectionWhenEndpointIsAdded() { + ConsoleRender render = new ConsoleRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("add_endpoint_1.yaml", "add_endpoint_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).contains("What's New"); + } + + @Test + public void renderShowsWhatsDeprecatedSectionWhenEndpointIsDeprecated() { + ConsoleRender render = new ConsoleRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("deprecate_endpoint_1.yaml", "deprecate_endpoint_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).contains("What's Deprecated"); + } + + @Test + public void renderShowsWhatsChangedSectionWithCorrectFormattingWhenEndpointIsChanged() { + ConsoleRender render = new ConsoleRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("change_endpoint_1.yaml", "change_endpoint_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()) + .contains("What's Changed") + .containsSubsequence("- GET /widgets", "Parameter:", "- Changed query-param-1 in query"); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java new file mode 100644 index 000000000..e62268d10 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java @@ -0,0 +1,22 @@ +package org.openapitools.openapidiff.core.output; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class HtmlRenderTest { + @Test + public void renderDoesNotFailWhenPropertyHasBeenRemoved() { + HtmlRender render = new HtmlRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/JsonRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/JsonRenderTest.java new file mode 100644 index 000000000..da5ce9ecc --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/JsonRenderTest.java @@ -0,0 +1,33 @@ +package org.openapitools.openapidiff.core.output; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class JsonRenderTest { + @Test + public void renderDoesNotFailWhenPropertyHasBeenRemoved() { + JsonRender render = new JsonRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderDoesNotFailForJsr310Types() { + JsonRender render = new JsonRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("jsr310_property_1.yaml", "jsr310_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java new file mode 100644 index 000000000..fb1f24fef --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java @@ -0,0 +1,43 @@ +package org.openapitools.openapidiff.core.output; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +public class MarkdownRenderTest { + @Test + public void renderDoesNotFailWhenPropertyHasBeenRemoved() { + MarkdownRender render = new MarkdownRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderDoesNotCauseStackOverflowWithRecursiveDefinitions() { + MarkdownRender render = new MarkdownRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = OpenApiCompare.fromLocations("recursive_old.yaml", "recursive_new.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } + + @Test + public void renderDoesNotFailWhenHTTPStatusCodeIsRange() { + MarkdownRender render = new MarkdownRender(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + ChangedOpenApi diff = + OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml"); + render.render(diff, outputStreamWriter); + assertThat(outputStream.toString()).isNotBlank(); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/RenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/RenderTest.java new file mode 100644 index 000000000..86f1e5842 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/output/RenderTest.java @@ -0,0 +1,27 @@ +package org.openapitools.openapidiff.core.output; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.openapitools.openapidiff.core.exception.RendererException; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; + +class RenderTest { + + private final Render testRenderImpl = + (diff, outputStreamWriter) -> { + try { + outputStreamWriter.write("Output"); + outputStreamWriter.close(); + } catch (IOException e) { + throw new RendererException(e); + } + }; + + @Test + void testDefaultRenderMethod() { + ChangedOpenApi diff = new ChangedOpenApi(null); + assertThat(testRenderImpl.render(diff)).isEqualTo("Output"); + } +} diff --git a/core/src/test/java/org/openapitools/openapidiff/core/utils/FileUtilsTest.java b/core/src/test/java/org/openapitools/openapidiff/core/utils/FileUtilsTest.java new file mode 100644 index 000000000..73c1e53b0 --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/utils/FileUtilsTest.java @@ -0,0 +1,59 @@ +package org.openapitools.openapidiff.core.utils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; +import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.output.ConsoleRender; + +class FileUtilsTest { + private ChangedOpenApi changedOpenApi; + + @BeforeEach + void setup() { + changedOpenApi = new ChangedOpenApi(OpenApiDiffOptions.builder().build()); + changedOpenApi.setChangedSchemas(Collections.emptyList()); + changedOpenApi.setChangedOperations(Collections.emptyList()); + changedOpenApi.setNewEndpoints(Collections.emptyList()); + changedOpenApi.setMissingEndpoints(Collections.emptyList()); + } + + @Test + void writeToFile_filenameIsNull_doesNothing() { + assertDoesNotThrow(() -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, null)); + } + + @Test + void writeToFile_filenameIsEmpty_doesNothing() { + assertDoesNotThrow( + () -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, StringUtils.EMPTY)); + } + + @Test + void writeToFile_fileExists_overwrites_file(@TempDir Path tempDir) throws IOException { + final Path path = tempDir.resolve("output.txt"); + Files.write(path, "Test".getBytes(StandardCharsets.UTF_8)); + + assertDoesNotThrow( + () -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, path.toString())); + assertThat(path).exists().content().isNotEqualTo("Test"); + } + + @Test + void writeToFile_fileDoesNotExist_createsFile(@TempDir Path tempDir) { + final Path path = tempDir.resolve("output.txt"); + assertDoesNotThrow( + () -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, path.toString())); + assertThat(path).exists().content().isNotBlank(); + } +} diff --git a/core/src/test/resources/add_endpoint_1.yaml b/core/src/test/resources/add_endpoint_1.yaml new file mode 100644 index 000000000..0c544c7b4 --- /dev/null +++ b/core/src/test/resources/add_endpoint_1.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/add_endpoint_2.yaml b/core/src/test/resources/add_endpoint_2.yaml new file mode 100644 index 000000000..95906047e --- /dev/null +++ b/core/src/test/resources/add_endpoint_2.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/{petId2}: + post: + tags: + - pet + summary: deletes a pet + description: '' + operationId: deletePet + deprecated: true + parameters: + - name: petId2 + in: path + description: Pet ID to delete + required: true + schema: + type: integer + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/backwardCompatibility/apiResponse_diff_1.yaml b/core/src/test/resources/backwardCompatibility/apiResponse_diff_1.yaml deleted file mode 100644 index fb1e6047d..000000000 --- a/core/src/test/resources/backwardCompatibility/apiResponse_diff_1.yaml +++ /dev/null @@ -1,7 +0,0 @@ -openapi: 3.0.0 -info: - title: Swagger Petstore -paths: - /store/inventory: - get: - operationId: asdf \ No newline at end of file diff --git a/core/src/test/resources/backwardCompatibility/apiResponse_diff_2.yaml b/core/src/test/resources/backwardCompatibility/apiResponse_diff_2.yaml deleted file mode 100644 index 82da7cfd6..000000000 --- a/core/src/test/resources/backwardCompatibility/apiResponse_diff_2.yaml +++ /dev/null @@ -1,15 +0,0 @@ -openapi: 3.0.0 -info: - title: Swagger Petstore -paths: - /store/inventory: - get: - responses: - '200': - content: - application/json: - schema: - type: object - properties: - title: - type: string \ No newline at end of file diff --git a/core/src/test/resources/backwardCompatibility/bc_1.yaml b/core/src/test/resources/backwardCompatibility/bc_1.yaml deleted file mode 100644 index 8247d2fcb..000000000 --- a/core/src/test/resources/backwardCompatibility/bc_1.yaml +++ /dev/null @@ -1,132 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - maxLength: 16 - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: object - properties: - pets: - type: array - items: - $ref: '#/components/schemas/Dog' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - Dog: - type: object - properties: - bark: - type: string - test: - writeOnly: true - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/backwardCompatibility/bc_2.yaml b/core/src/test/resources/backwardCompatibility/bc_2.yaml deleted file mode 100644 index 179f3ca3f..000000000 --- a/core/src/test/resources/backwardCompatibility/bc_2.yaml +++ /dev/null @@ -1,150 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - requestBody: - $ref: '#/components/requestBodies/Pet' - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/MyResponseType' - '405': - description: Invalid input - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - maxLength: 24 - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: object - properties: - pets: - type: array - items: - $ref: '#/components/schemas/Dog' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - Dog: - type: object - properties: - bark: - type: string - test: - writeOnly: true - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/backwardCompatibility/bc_3.yaml b/core/src/test/resources/backwardCompatibility/bc_3.yaml deleted file mode 100644 index 30a68ced7..000000000 --- a/core/src/test/resources/backwardCompatibility/bc_3.yaml +++ /dev/null @@ -1,163 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - requestBody: - $ref: '#/components/requestBodies/Pet' - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/MyResponseType' - '405': - description: Invalid input - get: - tags: - - pet - summary: Finds Pets by name - description: name can be provided for the pet - operationId: getPet - parameters: - - name: name - in: query - description: name that need to be considered for filter - required: true - schema: - type: string - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - maxLength: 36 - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: object - properties: - pets: - type: array - items: - $ref: '#/components/schemas/Dog' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - Dog: - type: object - properties: - bark: - type: string - test: - writeOnly: true - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/backwardCompatibility/bc_4.yaml b/core/src/test/resources/backwardCompatibility/bc_4.yaml deleted file mode 100644 index 4e6ee3e18..000000000 --- a/core/src/test/resources/backwardCompatibility/bc_4.yaml +++ /dev/null @@ -1,155 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - requestBody: - $ref: '#/components/requestBodies/Pet' - responses: - '200': - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/MyResponseType' - '405': - description: Invalid input - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - enum: - - available - - pending - - sold - default: available - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: object - properties: - pets: - type: array - items: - $ref: '#/components/schemas/Dog' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - deprecated: true - Dog: - type: object - properties: - bark: - type: string - test: - writeOnly: true - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/backwardCompatibility/bc_5.yaml b/core/src/test/resources/backwardCompatibility/bc_5.yaml deleted file mode 100644 index 6b282ba48..000000000 --- a/core/src/test/resources/backwardCompatibility/bc_5.yaml +++ /dev/null @@ -1,131 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /pet/findByStatus: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - maxLength: 16 - responses: - '200': - description: successful operation - content: - application/json: - schema: - type: object - properties: - pets: - type: array - items: - $ref: '#/components/schemas/Dog' - '400': - description: Invalid status value - security: - - petstore_auth: - - 'write:pets' - - 'read:pets' -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - Dog: - type: object - properties: - bark: - type: string - test: - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/bc_content_base.yaml b/core/src/test/resources/bc_content_base.yaml new file mode 100644 index 000000000..82634f2a9 --- /dev/null +++ b/core/src/test/resources/bc_content_base.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + application/text: + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + application/text: + schema: + type: string diff --git a/core/src/test/resources/bc_content_changed_but_compatible.yaml b/core/src/test/resources/bc_content_changed_but_compatible.yaml new file mode 100644 index 000000000..8300fbad7 --- /dev/null +++ b/core/src/test/resources/bc_content_changed_but_compatible.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + application/text: + schema: + type: string + application/xml: + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + application/text: + schema: + type: string + application/xml: + schema: + type: string diff --git a/core/src/test/resources/bc_enum_base.yaml b/core/src/test/resources/bc_enum_base.yaml new file mode 100644 index 000000000..fe020824a --- /dev/null +++ b/core/src/test/resources/bc_enum_base.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: param-inline-enum + in: query + required: true + schema: + type: string + enum: + - param-inline-enum-val-1 + - param-inline-enum-val-2 + default: param-inline-enum-val-1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + enum-prop: + type: string + enum: + - enum-prop-val-1 + - enum-prop-val-2 + default: enum-prop-val-1 diff --git a/core/src/test/resources/bc_enum_changed_but_compatible.yaml b/core/src/test/resources/bc_enum_changed_but_compatible.yaml new file mode 100644 index 000000000..7fd9b1da6 --- /dev/null +++ b/core/src/test/resources/bc_enum_changed_but_compatible.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: param-inline-enum + in: query + required: true + schema: + type: string + enum: + - param-inline-enum-val-1 + - param-inline-enum-val-2 + - param-inline-enum-val-3 + default: param-inline-enum-val-1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + enum-prop: + type: string + enum: + - enum-prop-val-1 + default: enum-prop-val-1 diff --git a/core/src/test/resources/bc_maxlength_base.yaml b/core/src/test/resources/bc_maxlength_base.yaml new file mode 100644 index 000000000..583adca5d --- /dev/null +++ b/core/src/test/resources/bc_maxlength_base.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: string + maxLength: 16 + application/xml: + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + maxLength: 16 + application/xml: + schema: + type: string diff --git a/core/src/test/resources/bc_numericrange_base.yaml b/core/src/test/resources/bc_numericrange_base.yaml new file mode 100644 index 000000000..e03c5939e --- /dev/null +++ b/core/src/test/resources/bc_numericrange_base.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_numericrange_changed_but_compatible.yaml b/core/src/test/resources/bc_numericrange_changed_but_compatible.yaml new file mode 100644 index 000000000..f7e5057f4 --- /dev/null +++ b/core/src/test/resources/bc_numericrange_changed_but_compatible.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + application/text: + schema: + type: integer + format: int32 + minimum: 9 + maximum: 21 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: false + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: true + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + application/xml: + schema: + type: integer + format: int32 + minimum: 11 + maximum: 19 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_oauthflow_authorization_url_changed.yaml b/core/src/test/resources/bc_oauthflow_authorization_url_changed.yaml new file mode 100644 index 000000000..d14957eea --- /dev/null +++ b/core/src/test/resources/bc_oauthflow_authorization_url_changed.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - oauth2-scheme-1: [] +components: + securitySchemes: + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/authorization2 + refreshUrl: https://example.com/api/oauth/refresh + tokenUrl: https://example.com/api/oauth/token + scopes: {} diff --git a/core/src/test/resources/bc_oauthflow_base.yaml b/core/src/test/resources/bc_oauthflow_base.yaml new file mode 100644 index 000000000..40c7509b4 --- /dev/null +++ b/core/src/test/resources/bc_oauthflow_base.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - oauth2-scheme-1: [] +components: + securitySchemes: + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/authorization + refreshUrl: https://example.com/api/oauth/refresh + tokenUrl: https://example.com/api/oauth/token + scopes: {} diff --git a/core/src/test/resources/bc_oauthflow_refresh_url_changed.yaml b/core/src/test/resources/bc_oauthflow_refresh_url_changed.yaml new file mode 100644 index 000000000..bde8f2285 --- /dev/null +++ b/core/src/test/resources/bc_oauthflow_refresh_url_changed.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - oauth2-scheme-1: [] +components: + securitySchemes: + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/authorization + refreshUrl: https://example.com/api/oauth/refresh2 + tokenUrl: https://example.com/api/oauth/token + scopes: {} diff --git a/core/src/test/resources/bc_oauthflow_token_url_changed.yaml b/core/src/test/resources/bc_oauthflow_token_url_changed.yaml new file mode 100644 index 000000000..6992e3c07 --- /dev/null +++ b/core/src/test/resources/bc_oauthflow_token_url_changed.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - oauth2-scheme-1: [] +components: + securitySchemes: + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/authorization + refreshUrl: https://example.com/api/oauth/refresh + tokenUrl: https://example.com/api/oauth/token2 + scopes: {} diff --git a/core/src/test/resources/bc_oneof_base.yaml b/core/src/test/resources/bc_oneof_base.yaml new file mode 100644 index 000000000..b5ef92c35 --- /dev/null +++ b/core/src/test/resources/bc_oneof_base.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' +components: + schemas: + WidgetCreateRequest: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + WidgetCreateResponse: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + Doodad: + type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_oneof_changed_but_compatible.yaml b/core/src/test/resources/bc_oneof_changed_but_compatible.yaml new file mode 100644 index 000000000..78468ebe0 --- /dev/null +++ b/core/src/test/resources/bc_oneof_changed_but_compatible.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' +components: + schemas: + WidgetCreateRequest: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + - $ref: '#/components/schemas/Gizmo' + WidgetCreateResponse: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + Doodad: + type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_openapi_base.yaml b/core/src/test/resources/bc_openapi_base.yaml new file mode 100644 index 000000000..78e86b943 --- /dev/null +++ b/core/src/test/resources/bc_openapi_base.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + post: + operationId: widgetCreate + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_openapi_changed_but_compatible.yaml b/core/src/test/resources/bc_openapi_changed_but_compatible.yaml new file mode 100644 index 000000000..5b5dfffc1 --- /dev/null +++ b/core/src/test/resources/bc_openapi_changed_but_compatible.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + post: + operationId: widgetCreate + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + /widgets/{id}: + get: + operationId: getWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_openapi_endpoints_decreased.yaml b/core/src/test/resources/bc_openapi_endpoints_decreased.yaml new file mode 100644 index 000000000..bd33b69dd --- /dev/null +++ b/core/src/test/resources/bc_openapi_endpoints_decreased.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_operation_base.yaml b/core/src/test/resources/bc_operation_base.yaml new file mode 100644 index 000000000..eb6403307 --- /dev/null +++ b/core/src/test/resources/bc_operation_base.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_operation_changed_but_compatible.yaml b/core/src/test/resources/bc_operation_changed_but_compatible.yaml new file mode 100644 index 000000000..2d99878b8 --- /dev/null +++ b/core/src/test/resources/bc_operation_changed_but_compatible.yaml @@ -0,0 +1,17 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + deprecated: true + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_path_base.yaml b/core/src/test/resources/bc_path_base.yaml new file mode 100644 index 000000000..78e86b943 --- /dev/null +++ b/core/src/test/resources/bc_path_base.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + post: + operationId: widgetCreate + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_path_changed_but_compatible.yaml b/core/src/test/resources/bc_path_changed_but_compatible.yaml new file mode 100644 index 000000000..d2ae98018 --- /dev/null +++ b/core/src/test/resources/bc_path_changed_but_compatible.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + post: + operationId: widgetCreate + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + put: + operationId: widgetUpdate + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_path_ops_decreased.yaml b/core/src/test/resources/bc_path_ops_decreased.yaml new file mode 100644 index 000000000..eb6403307 --- /dev/null +++ b/core/src/test/resources/bc_path_ops_decreased.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_paths_base.yaml b/core/src/test/resources/bc_paths_base.yaml new file mode 100644 index 000000000..451f66310 --- /dev/null +++ b/core/src/test/resources/bc_paths_base.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + /widgets/{id}: + get: + operationId: getWidget + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_paths_changed_but_compatible.yaml b/core/src/test/resources/bc_paths_changed_but_compatible.yaml new file mode 100644 index 000000000..e2b94c228 --- /dev/null +++ b/core/src/test/resources/bc_paths_changed_but_compatible.yaml @@ -0,0 +1,36 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + /widgets/{id}: + get: + operationId: getWidget + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + /widgets/{id}/status: + get: + operationId: getWidgetStatus + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_paths_decreased.yaml b/core/src/test/resources/bc_paths_decreased.yaml new file mode 100644 index 000000000..eb6403307 --- /dev/null +++ b/core/src/test/resources/bc_paths_decreased.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_readonly_base.yaml b/core/src/test/resources/bc_readonly_base.yaml new file mode 100644 index 000000000..dc4889b1f --- /dev/null +++ b/core/src/test/resources/bc_readonly_base.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + readOnly: true + prop2: + type: string + required: + - prop1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + readOnly: true diff --git a/core/src/test/resources/bc_readonly_changed_but_compatible.yaml b/core/src/test/resources/bc_readonly_changed_but_compatible.yaml new file mode 100644 index 000000000..cc6596ef6 --- /dev/null +++ b/core/src/test/resources/bc_readonly_changed_but_compatible.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + readOnly: true + prop2: + type: string + required: + - prop1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string diff --git a/core/src/test/resources/bc_request_body_base.yaml b/core/src/test/resources/bc_request_body_base.yaml new file mode 100644 index 000000000..fd89553e9 --- /dev/null +++ b/core/src/test/resources/bc_request_body_base.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_body_required_changed.yaml b/core/src/test/resources/bc_request_body_required_changed.yaml new file mode 100644 index 000000000..859512ca9 --- /dev/null +++ b/core/src/test/resources/bc_request_body_required_changed.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: string + required: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_content_decreased.yaml b/core/src/test/resources/bc_request_content_decreased.yaml new file mode 100644 index 000000000..d7a5517de --- /dev/null +++ b/core/src/test/resources/bc_request_content_decreased.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + application/text: + schema: + type: string diff --git a/core/src/test/resources/bc_request_enum_decreased.yaml b/core/src/test/resources/bc_request_enum_decreased.yaml new file mode 100644 index 000000000..c131e9aa7 --- /dev/null +++ b/core/src/test/resources/bc_request_enum_decreased.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: param-inline-enum + in: query + required: true + schema: + type: string + enum: + - param-inline-enum-val-1 + default: param-inline-enum-val-1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + enum-prop: + type: string + enum: + - enum-prop-val-1 + - enum-prop-val-2 + default: enum-prop-val-1 diff --git a/core/src/test/resources/bc_request_maxlength_decreased.yaml b/core/src/test/resources/bc_request_maxlength_decreased.yaml new file mode 100644 index 000000000..48d50863c --- /dev/null +++ b/core/src/test/resources/bc_request_maxlength_decreased.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: string + maxLength: 10 + application/xml: + schema: + type: string + maxLength: 10 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + maxLength: 16 + application/xml: + schema: + type: string diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_max_created.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_max_created.yaml new file mode 100644 index 000000000..f893a9545 --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_exclusive_max_created.yaml @@ -0,0 +1,56 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMaximum: true + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_max_set.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_max_set.yaml new file mode 100644 index 000000000..efa02f990 --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_exclusive_max_set.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: true + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_min_created.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_min_created.yaml new file mode 100644 index 000000000..649b7f48b --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_exclusive_min_created.yaml @@ -0,0 +1,56 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_min_set.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_min_set.yaml new file mode 100644 index 000000000..e6d0c81fb --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_exclusive_min_set.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: true + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_max_added.yaml b/core/src/test/resources/bc_request_numericrange_max_added.yaml new file mode 100644 index 000000000..f438d9bda --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_max_added.yaml @@ -0,0 +1,56 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + maximum: 20 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_max_decreased.yaml b/core/src/test/resources/bc_request_numericrange_max_decreased.yaml new file mode 100644 index 000000000..be0dec2c6 --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_max_decreased.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 19 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 19 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_min_added.yaml b/core/src/test/resources/bc_request_numericrange_min_added.yaml new file mode 100644 index 000000000..84a06bbca --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_min_added.yaml @@ -0,0 +1,56 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + minimum: 10 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_numericrange_min_increased.yaml b/core/src/test/resources/bc_request_numericrange_min_increased.yaml new file mode 100644 index 000000000..87df1ba4a --- /dev/null +++ b/core/src/test/resources/bc_request_numericrange_min_increased.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 11 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 11 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_request_oneof_decreased.yaml b/core/src/test/resources/bc_request_oneof_decreased.yaml new file mode 100644 index 000000000..82371aed6 --- /dev/null +++ b/core/src/test/resources/bc_request_oneof_decreased.yaml @@ -0,0 +1,47 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' +components: + schemas: + WidgetCreateRequest: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + WidgetCreateResponse: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + Doodad: + type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_request_param_allowemptyvalue_decreased.yaml b/core/src/test/resources/bc_request_param_allowemptyvalue_decreased.yaml new file mode 100644 index 000000000..cf25dd898 --- /dev/null +++ b/core/src/test/resources/bc_request_param_allowemptyvalue_decreased.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_param_base.yaml b/core/src/test/resources/bc_request_param_base.yaml new file mode 100644 index 000000000..555015991 --- /dev/null +++ b/core/src/test/resources/bc_request_param_base.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: true + allowEmptyValue: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_param_changed_but_compatible.yaml b/core/src/test/resources/bc_request_param_changed_but_compatible.yaml new file mode 100644 index 000000000..281fd0514 --- /dev/null +++ b/core/src/test/resources/bc_request_param_changed_but_compatible.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + allowEmptyValue: true + required: false + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: true + allowEmptyValue: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_param_explode_changed.yaml b/core/src/test/resources/bc_request_param_explode_changed.yaml new file mode 100644 index 000000000..3424c5fbe --- /dev/null +++ b/core/src/test/resources/bc_request_param_explode_changed.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + explode: true + required: true + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: false + allowEmptyValue: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_param_required_increased.yaml b/core/src/test/resources/bc_request_param_required_increased.yaml new file mode 100644 index 000000000..6af74a9bc --- /dev/null +++ b/core/src/test/resources/bc_request_param_required_increased.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: true + allowEmptyValue: true + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_param_style_changed.yaml b/core/src/test/resources/bc_request_param_style_changed.yaml new file mode 100644 index 000000000..ff8cb1dba --- /dev/null +++ b/core/src/test/resources/bc_request_param_style_changed.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + style: form + schema: + type: string + - name: query-param-2 + in: query + style: simple + explode: true + allowEmptyValue: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_params_base.yaml b/core/src/test/resources/bc_request_params_base.yaml new file mode 100644 index 000000000..9d623821f --- /dev/null +++ b/core/src/test/resources/bc_request_params_base.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_params_changed_but_compatible.yaml b/core/src/test/resources/bc_request_params_changed_but_compatible.yaml new file mode 100644 index 000000000..62a26fbb6 --- /dev/null +++ b/core/src/test/resources/bc_request_params_changed_but_compatible.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + schema: + type: string + - name: query-param-3 + in: query + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_params_decreased.yaml b/core/src/test/resources/bc_request_params_decreased.yaml new file mode 100644 index 000000000..2837bcc4d --- /dev/null +++ b/core/src/test/resources/bc_request_params_decreased.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_params_required_increased.yaml b/core/src/test/resources/bc_request_params_required_increased.yaml new file mode 100644 index 000000000..d1c2e16c3 --- /dev/null +++ b/core/src/test/resources/bc_request_params_required_increased.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + schema: + type: string + - name: query-param-3 + in: query + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/bc_request_readonly_increased.yaml b/core/src/test/resources/bc_request_readonly_increased.yaml new file mode 100644 index 000000000..5de3149d1 --- /dev/null +++ b/core/src/test/resources/bc_request_readonly_increased.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + readOnly: true + prop2: + type: string + readOnly: true + required: + - prop1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + readOnly: true diff --git a/core/src/test/resources/bc_request_readonly_required_decreased.yaml b/core/src/test/resources/bc_request_readonly_required_decreased.yaml new file mode 100644 index 000000000..73f12cae5 --- /dev/null +++ b/core/src/test/resources/bc_request_readonly_required_decreased.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + required: + - prop1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + readOnly: true diff --git a/core/src/test/resources/bc_request_required_increased.yaml b/core/src/test/resources/bc_request_required_increased.yaml new file mode 100644 index 000000000..cd9821ac0 --- /dev/null +++ b/core/src/test/resources/bc_request_required_increased.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + - prop2 + - prop3 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + - prop2 diff --git a/core/src/test/resources/bc_request_schema_format_decreased.yaml b/core/src/test/resources/bc_request_schema_format_decreased.yaml new file mode 100644 index 000000000..3a69caf83 --- /dev/null +++ b/core/src/test/resources/bc_request_schema_format_decreased.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int32 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_request_schema_format_increased.yaml b/core/src/test/resources/bc_request_schema_format_increased.yaml new file mode 100644 index 000000000..6201e14c9 --- /dev/null +++ b/core/src/test/resources/bc_request_schema_format_increased.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int64 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_request_schema_props_put_increased.yaml b/core/src/test/resources/bc_request_schema_props_put_increased.yaml new file mode 100644 index 000000000..9ca619a47 --- /dev/null +++ b/core/src/test/resources/bc_request_schema_props_put_increased.yaml @@ -0,0 +1,117 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + put_prop3: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_required_base.yaml b/core/src/test/resources/bc_required_base.yaml new file mode 100644 index 000000000..711cd20a0 --- /dev/null +++ b/core/src/test/resources/bc_required_base.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + - prop2 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + - prop2 diff --git a/core/src/test/resources/bc_required_changed_but_compatible.yaml b/core/src/test/resources/bc_required_changed_but_compatible.yaml new file mode 100644 index 000000000..4e352896f --- /dev/null +++ b/core/src/test/resources/bc_required_changed_but_compatible.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + - prop2 + - prop3 diff --git a/core/src/test/resources/bc_response_apiresponse_base.yaml b/core/src/test/resources/bc_response_apiresponse_base.yaml new file mode 100644 index 000000000..640a7dc1a --- /dev/null +++ b/core/src/test/resources/bc_response_apiresponse_base.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + '400': + description: Invalid status value + /widgets/index: + post: + operationId: indexWidgets + description: Regression test for https://github.com/OpenAPITools/openapi-diff/pull/206 (handle missing responses) diff --git a/core/src/test/resources/bc_response_apiresponse_changed_but_compatible.yaml b/core/src/test/resources/bc_response_apiresponse_changed_but_compatible.yaml new file mode 100644 index 000000000..6ad621348 --- /dev/null +++ b/core/src/test/resources/bc_response_apiresponse_changed_but_compatible.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + '201': + description: OK + '400': + description: Invalid status value + /widgets/index: + post: + operationId: indexWidgets + responses: + '200': + description: successful operation diff --git a/core/src/test/resources/bc_response_apiresponse_decreased.yaml b/core/src/test/resources/bc_response_apiresponse_decreased.yaml new file mode 100644 index 000000000..6502cf0ce --- /dev/null +++ b/core/src/test/resources/bc_response_apiresponse_decreased.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + /widgets/index: + post: + operationId: indexWidgets + description: Regression test for https://github.com/OpenAPITools/openapi-diff/pull/206 (handle missing responses) diff --git a/core/src/test/resources/bc_response_content_decreased.yaml b/core/src/test/resources/bc_response_content_decreased.yaml new file mode 100644 index 000000000..2915db537 --- /dev/null +++ b/core/src/test/resources/bc_response_content_decreased.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + application/text: + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 diff --git a/core/src/test/resources/bc_response_enum_increased.yaml b/core/src/test/resources/bc_response_enum_increased.yaml new file mode 100644 index 000000000..27a0d58d0 --- /dev/null +++ b/core/src/test/resources/bc_response_enum_increased.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: param-inline-enum + in: query + required: true + schema: + type: string + enum: + - param-inline-enum-val-1 + - param-inline-enum-val-2 + default: param-inline-enum-val-1 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + enum-prop: + type: string + enum: + - enum-prop-val-1 + - enum-prop-val-2 + - enum-prop-val-3 + default: enum-prop-val-1 diff --git a/core/src/test/resources/bc_response_header_base.yaml b/core/src/test/resources/bc_response_header_base.yaml new file mode 100644 index 000000000..1680fee21 --- /dev/null +++ b/core/src/test/resources/bc_response_header_base.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 + X-Header-2: + schema: + type: integer + X-Header-3: + schema: + type: integer + explode: false + X-Header-4: + schema: + type: integer + explode: true diff --git a/core/src/test/resources/bc_response_header_changed_but_compatible.yaml b/core/src/test/resources/bc_response_header_changed_but_compatible.yaml new file mode 100644 index 000000000..b660903db --- /dev/null +++ b/core/src/test/resources/bc_response_header_changed_but_compatible.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 + deprecated: false + X-Header-2: + schema: + type: integer + deprecated: true + X-Header-3: + schema: + type: integer + explode: false + X-Header-4: + schema: + type: integer + explode: true diff --git a/core/src/test/resources/bc_response_header_explode_changed.yaml b/core/src/test/resources/bc_response_header_explode_changed.yaml new file mode 100644 index 000000000..3403c28eb --- /dev/null +++ b/core/src/test/resources/bc_response_header_explode_changed.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 + X-Header-2: + schema: + type: integer + explode: true + X-Header-3: + schema: + type: integer + X-Header-4: + schema: + type: integer + explode: false diff --git a/core/src/test/resources/bc_response_header_required_decreased.yaml b/core/src/test/resources/bc_response_header_required_decreased.yaml new file mode 100644 index 000000000..9b457cdc7 --- /dev/null +++ b/core/src/test/resources/bc_response_header_required_decreased.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + schema: + type: integer + format: int32 + X-Header-2: + schema: + type: integer + X-Header-3: + schema: + type: integer + explode: false + X-Header-4: + schema: + type: integer + explode: true diff --git a/core/src/test/resources/bc_response_header_required_increased.yaml b/core/src/test/resources/bc_response_header_required_increased.yaml new file mode 100644 index 000000000..7d76aec7e --- /dev/null +++ b/core/src/test/resources/bc_response_header_required_increased.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 + X-Header-2: + schema: + type: integer + required: true + X-Header-3: + schema: + type: integer + explode: false + X-Header-4: + schema: + type: integer + explode: true diff --git a/core/src/test/resources/bc_response_headers_base.yaml b/core/src/test/resources/bc_response_headers_base.yaml new file mode 100644 index 000000000..17716ba06 --- /dev/null +++ b/core/src/test/resources/bc_response_headers_base.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 + X-Header-2: + schema: + type: integer diff --git a/core/src/test/resources/bc_response_headers_changed_but_compatible.yaml b/core/src/test/resources/bc_response_headers_changed_but_compatible.yaml new file mode 100644 index 000000000..837b3e222 --- /dev/null +++ b/core/src/test/resources/bc_response_headers_changed_but_compatible.yaml @@ -0,0 +1,24 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 + X-Header-2: + schema: + type: integer + X-Header-3: + schema: + type: integer diff --git a/core/src/test/resources/bc_response_headers_decreased.yaml b/core/src/test/resources/bc_response_headers_decreased.yaml new file mode 100644 index 000000000..c5166c9c2 --- /dev/null +++ b/core/src/test/resources/bc_response_headers_decreased.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + headers: + X-Header-1: + required: true + schema: + type: integer + format: int32 diff --git a/core/src/test/resources/bc_response_maxlength_increased.yaml b/core/src/test/resources/bc_response_maxlength_increased.yaml new file mode 100644 index 000000000..40dd4309d --- /dev/null +++ b/core/src/test/resources/bc_response_maxlength_increased.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: string + maxLength: 16 + application/xml: + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + maxLength: 20 + application/xml: + schema: + type: string + maxLength: 20 diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_max_deleted.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_max_deleted.yaml new file mode 100644 index 000000000..385b4759d --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_exclusive_max_deleted.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_max_unset.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_max_unset.yaml new file mode 100644 index 000000000..e6c1448cd --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_exclusive_max_unset.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: false diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_min_deleted.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_min_deleted.yaml new file mode 100644 index 000000000..49881a7f4 --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_exclusive_min_deleted.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_min_unset.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_min_unset.yaml new file mode 100644 index 000000000..75bb758fa --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_exclusive_min_unset.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: false + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_response_numericrange_max_deleted.yaml b/core/src/test/resources/bc_response_numericrange_max_deleted.yaml new file mode 100644 index 000000000..6edf439f2 --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_max_deleted.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_response_numericrange_max_increased.yaml b/core/src/test/resources/bc_response_numericrange_max_increased.yaml new file mode 100644 index 000000000..e994bf88c --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_max_increased.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 21 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 21 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_response_numericrange_min_decreased.yaml b/core/src/test/resources/bc_response_numericrange_min_decreased.yaml new file mode 100644 index 000000000..2055a3247 --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_min_decreased.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 9 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 9 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_response_numericrange_min_deleted.yaml b/core/src/test/resources/bc_response_numericrange_min_deleted.yaml new file mode 100644 index 000000000..087245c07 --- /dev/null +++ b/core/src/test/resources/bc_response_numericrange_min_deleted.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + minimum: 10 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: integer + format: int32 + exclusiveMinimum: false + exclusiveMaximum: false + application/text: + schema: + type: integer + format: int32 + maximum: 20 + application/xml: + schema: + type: integer + format: int32 + maximum: 20 + exclusiveMinimum: true + exclusiveMaximum: true diff --git a/core/src/test/resources/bc_response_oneof_increased.yaml b/core/src/test/resources/bc_response_oneof_increased.yaml new file mode 100644 index 000000000..0d6636e0e --- /dev/null +++ b/core/src/test/resources/bc_response_oneof_increased.yaml @@ -0,0 +1,49 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' +components: + schemas: + WidgetCreateRequest: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + WidgetCreateResponse: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + - $ref: '#/components/schemas/Gizmo' + Doodad: + type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_response_required_decreased.yaml b/core/src/test/resources/bc_response_required_decreased.yaml new file mode 100644 index 000000000..e77d73da4 --- /dev/null +++ b/core/src/test/resources/bc_response_required_decreased.yaml @@ -0,0 +1,40 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 + - prop2 + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + prop3: + type: string + required: + - prop1 diff --git a/core/src/test/resources/bc_response_schema_format_decreased.yaml b/core/src/test/resources/bc_response_schema_format_decreased.yaml new file mode 100644 index 000000000..66ad527df --- /dev/null +++ b/core/src/test/resources/bc_response_schema_format_decreased.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop2: + type: integer + format: int32 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_response_schema_format_increased.yaml b/core/src/test/resources/bc_response_schema_format_increased.yaml new file mode 100644 index 000000000..1ecc86fb6 --- /dev/null +++ b/core/src/test/resources/bc_response_schema_format_increased.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int64 + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_response_schema_props_required_decreased.yaml b/core/src/test/resources/bc_response_schema_props_required_decreased.yaml new file mode 100644 index 000000000..cbd2c5a44 --- /dev/null +++ b/core/src/test/resources/bc_response_schema_props_required_decreased.yaml @@ -0,0 +1,112 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_response_writeonly_increased.yaml b/core/src/test/resources/bc_response_writeonly_increased.yaml new file mode 100644 index 000000000..a009b8dc6 --- /dev/null +++ b/core/src/test/resources/bc_response_writeonly_increased.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + writeOnly: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + writeOnly: true + prop2: + type: string + writeOnly: true + required: + - prop1 diff --git a/core/src/test/resources/bc_response_writeonly_required_decreased.yaml b/core/src/test/resources/bc_response_writeonly_required_decreased.yaml new file mode 100644 index 000000000..74bf153ef --- /dev/null +++ b/core/src/test/resources/bc_response_writeonly_required_decreased.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + writeOnly: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + required: + - prop1 diff --git a/core/src/test/resources/bc_schema_base.yaml b/core/src/test/resources/bc_schema_base.yaml new file mode 100644 index 000000000..28d8800d8 --- /dev/null +++ b/core/src/test/resources/bc_schema_base.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_schema_changed_but_compatible.yaml b/core/src/test/resources/bc_schema_changed_but_compatible.yaml new file mode 100644 index 000000000..e8a6e7e94 --- /dev/null +++ b/core/src/test/resources/bc_schema_changed_but_compatible.yaml @@ -0,0 +1,114 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop2: + type: integer + format: int64 + request_prop3: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop3: + type: string + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + deprecated: true + prop2: + type: integer + format: int32 + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_schema_discriminator_changed.yaml b/core/src/test/resources/bc_schema_discriminator_changed.yaml new file mode 100644 index 000000000..fd9569001 --- /dev/null +++ b/core/src/test/resources/bc_schema_discriminator_changed.yaml @@ -0,0 +1,115 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: integer + format: int32 + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: 'prop1' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_schema_type_changed.yaml b/core/src/test/resources/bc_schema_type_changed.yaml new file mode 100644 index 000000000..946d16890 --- /dev/null +++ b/core/src/test/resources/bc_schema_type_changed.yaml @@ -0,0 +1,114 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateRequest' + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/WidgetCreateResponse' + put: + operationId: widgetUpdate + requestBody: + content: + application/json: + schema: + type: object + properties: + put_prop1: + type: string + put_prop2: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string +components: + schemas: + WidgetCreateRequest: + type: object + properties: + to_create: + $ref: '#/components/schemas/Widget_Polymorphic' + request_prop1: + type: integer + format: int32 + request_prop2: + type: integer + format: int64 + required: + - to_create + - request_prop1 + WidgetCreateResponse: + type: object + properties: + created: + $ref: '#/components/schemas/Widget_Polymorphic' + response_prop1: + type: string + response_prop2: + type: integer + format: int64 + required: + - created + - response_prop1 + Widget_Polymorphic: + type: object + oneOf: + - $ref: '#/components/schemas/Doodad' + - $ref: '#/components/schemas/Gadget' + discriminator: + propertyName: '@type' + Widget: + type: object + properties: + '@type': + type: string + prop1: + type: string + prop2: + type: integer + format: int32 + deprecated: true + required: + - '@type' + - prop1 + Doodad: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + doodad_prop1: + type: string + Gadget: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gadget_prop1: + type: string + Gizmo: + type: object + allOf: + - $ref: '#/components/schemas/Widget' + - type: object + properties: + gizmo_prop1: + type: string diff --git a/core/src/test/resources/bc_security_requirement_base.yaml b/core/src/test/resources/bc_security_requirement_base.yaml new file mode 100644 index 000000000..477736519 --- /dev/null +++ b/core/src/test/resources/bc_security_requirement_base.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] + my-scheme-2: [] +components: + securitySchemes: + my-scheme-1: + type: http + scheme: basic + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_requirement_changed_but_compatible.yaml b/core/src/test/resources/bc_security_requirement_changed_but_compatible.yaml new file mode 100644 index 000000000..9a58c327f --- /dev/null +++ b/core/src/test/resources/bc_security_requirement_changed_but_compatible.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] +components: + securitySchemes: + my-scheme-1: + type: http + scheme: basic + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_requirement_schemes_increased.yaml b/core/src/test/resources/bc_security_requirement_schemes_increased.yaml new file mode 100644 index 000000000..234aa7a29 --- /dev/null +++ b/core/src/test/resources/bc_security_requirement_schemes_increased.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] + my-scheme-2: [] + my-scheme-3: [] +components: + securitySchemes: + my-scheme-1: + type: http + scheme: basic + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_requirements_base.yaml b/core/src/test/resources/bc_security_requirements_base.yaml new file mode 100644 index 000000000..9c4c04650 --- /dev/null +++ b/core/src/test/resources/bc_security_requirements_base.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] + - my-scheme-2: [] +components: + securitySchemes: + my-scheme-1: + type: http + scheme: basic + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_requirements_changed_but_compatible.yaml b/core/src/test/resources/bc_security_requirements_changed_but_compatible.yaml new file mode 100644 index 000000000..ba983bc36 --- /dev/null +++ b/core/src/test/resources/bc_security_requirements_changed_but_compatible.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] + - my-scheme-2: [] + - my-scheme-3: [] +components: + securitySchemes: + my-scheme-1: + type: http + scheme: basic + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_requirements_decreased.yaml b/core/src/test/resources/bc_security_requirements_decreased.yaml new file mode 100644 index 000000000..9a58c327f --- /dev/null +++ b/core/src/test/resources/bc_security_requirements_decreased.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] +components: + securitySchemes: + my-scheme-1: + type: http + scheme: basic + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_requirements_scheme_type_changed.yaml b/core/src/test/resources/bc_security_requirements_scheme_type_changed.yaml new file mode 100644 index 000000000..d2e6f1a95 --- /dev/null +++ b/core/src/test/resources/bc_security_requirements_scheme_type_changed.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - my-scheme-1: [] + - my-scheme-2: [] +components: + securitySchemes: + my-scheme-1: + type: apiKey + name: api_key + in: header + my-scheme-2: + type: http + scheme: basic + my-scheme-3: + type: http + scheme: basic diff --git a/core/src/test/resources/bc_security_scheme_base.yaml b/core/src/test/resources/bc_security_scheme_base.yaml new file mode 100644 index 000000000..00caad137 --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_base.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: http + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_bearer_format_changed.yaml b/core/src/test/resources/bc_security_scheme_bearer_format_changed.yaml new file mode 100644 index 000000000..bf7fcca56 --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_bearer_format_changed.yaml @@ -0,0 +1,43 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: http + bearerFormat: myBearerFormat + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_changed_but_compatible.yaml b/core/src/test/resources/bc_security_scheme_changed_but_compatible.yaml new file mode 100644 index 000000000..5cf1f26bf --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_changed_but_compatible.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: http + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_in_changed.yaml b/core/src/test/resources/bc_security_scheme_in_changed.yaml new file mode 100644 index 000000000..5d937c361 --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_in_changed.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: query + basic-scheme-1: + type: http + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_open_id_connect_url_changed.yaml b/core/src/test/resources/bc_security_scheme_open_id_connect_url_changed.yaml new file mode 100644 index 000000000..e981a945f --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_open_id_connect_url_changed.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: http + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect2 + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_scheme_changed.yaml b/core/src/test/resources/bc_security_scheme_scheme_changed.yaml new file mode 100644 index 000000000..5b474049d --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_scheme_changed.yaml @@ -0,0 +1,43 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: http + scheme: Digest + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_scopes_increased.yaml b/core/src/test/resources/bc_security_scheme_scopes_increased.yaml new file mode 100644 index 000000000..8cfe76833 --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_scopes_increased.yaml @@ -0,0 +1,43 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" + - "scope3" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: http + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_security_scheme_type_changed.yaml b/core/src/test/resources/bc_security_scheme_type_changed.yaml new file mode 100644 index 000000000..985a1a17b --- /dev/null +++ b/core/src/test/resources/bc_security_scheme_type_changed.yaml @@ -0,0 +1,42 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string + security: + - basic-scheme-1: [] + - apikey-scheme-1: [] + - openidconnect-scheme-1: [] + - oauth2-scheme-1: + - "scope1" + - "scope2" +components: + securitySchemes: + apikey-scheme-1: + type: apiKey + name: api_key + in: header + basic-scheme-1: + type: apiKey + openidconnect-scheme-1: + type: openIdConnect + openIdConnectUrl: https://example.com/api/openidconnect + oauth2-scheme-1: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + scope1: scope1 description + scope2: scope2 description diff --git a/core/src/test/resources/bc_writeonly_base.yaml b/core/src/test/resources/bc_writeonly_base.yaml new file mode 100644 index 000000000..ad3c869db --- /dev/null +++ b/core/src/test/resources/bc_writeonly_base.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + writeOnly: true + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + writeOnly: true + prop2: + type: string + required: + - prop1 diff --git a/core/src/test/resources/bc_writeonly_changed_but_compatible.yaml b/core/src/test/resources/bc_writeonly_changed_but_compatible.yaml new file mode 100644 index 000000000..48857215b --- /dev/null +++ b/core/src/test/resources/bc_writeonly_changed_but_compatible.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + post: + operationId: widgetCreate + requestBody: + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + writeOnly: true + prop2: + type: string + required: + - prop1 diff --git a/core/src/test/resources/change_endpoint_1.yaml b/core/src/test/resources/change_endpoint_1.yaml new file mode 100644 index 000000000..555015991 --- /dev/null +++ b/core/src/test/resources/change_endpoint_1.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: true + allowEmptyValue: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/change_endpoint_2.yaml b/core/src/test/resources/change_endpoint_2.yaml new file mode 100644 index 000000000..281fd0514 --- /dev/null +++ b/core/src/test/resources/change_endpoint_2.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + allowEmptyValue: true + required: false + schema: + type: string + - name: query-param-2 + in: query + style: form + explode: true + allowEmptyValue: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: string diff --git a/core/src/test/resources/delete_endpoint_1.yaml b/core/src/test/resources/delete_endpoint_1.yaml new file mode 100644 index 000000000..59a315cd0 --- /dev/null +++ b/core/src/test/resources/delete_endpoint_1.yaml @@ -0,0 +1,52 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/{petId2}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId2 + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/delete_endpoint_2.yaml b/core/src/test/resources/delete_endpoint_2.yaml new file mode 100644 index 000000000..0c544c7b4 --- /dev/null +++ b/core/src/test/resources/delete_endpoint_2.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/deprecate_endpoint_1.yaml b/core/src/test/resources/deprecate_endpoint_1.yaml new file mode 100644 index 000000000..6d08cb721 --- /dev/null +++ b/core/src/test/resources/deprecate_endpoint_1.yaml @@ -0,0 +1,52 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/{petId2}: + post: + tags: + - pet + summary: deletes a pet + description: '' + operationId: deletePet + parameters: + - name: petId2 + in: path + description: Pet ID to delete + required: true + schema: + type: integer + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/deprecate_endpoint_2.yaml b/core/src/test/resources/deprecate_endpoint_2.yaml new file mode 100644 index 000000000..95906047e --- /dev/null +++ b/core/src/test/resources/deprecate_endpoint_2.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/{petId2}: + post: + tags: + - pet + summary: deletes a pet + description: '' + operationId: deletePet + deprecated: true + parameters: + - name: petId2 + in: path + description: Pet ID to delete + required: true + schema: + type: integer + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/header_1.yaml b/core/src/test/resources/header_1.yaml deleted file mode 100644 index 66f4a1665..000000000 --- a/core/src/test/resources/header_1.yaml +++ /dev/null @@ -1,129 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /user/login: - get: - tags: - - user - summary: Logs user into the system - description: '' - operationId: loginUser - parameters: - - name: username - in: query - description: The user name for login - required: true - schema: - type: string - responses: - '200': - description: successful operation - headers: - X-Rate-Limit: - description: calls per hour allowed by the user - schema: - type: integer - format: int32 - X-Expires-After: - description: date in UTC when token expires - schema: - type: integer - content: - application/xml: - schema: - type: integer - application/json: - schema: - type: string - '400': - description: Invalid username/password supplied -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - Dog: - type: object - properties: - bark: - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/header_2.yaml b/core/src/test/resources/header_2.yaml deleted file mode 100644 index 124b75ef3..000000000 --- a/core/src/test/resources/header_2.yaml +++ /dev/null @@ -1,129 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - /user/login: - get: - tags: - - user - summary: Logs user into the system - description: '' - operationId: loginUser - parameters: - - name: username - in: query - description: The user name for login - required: true - schema: - type: string - responses: - '200': - description: successful operation - headers: - X-Rate-Limit-New: - description: calls per hour allowed by the user - schema: - type: integer - format: int32 - X-Expires-After: - description: date in UTC when token expires - schema: - type: string - content: - application/xml: - schema: - type: integer - application/json: - schema: - type: string - '400': - description: Invalid username/password supplied -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: 'http://petstore.swagger.io/oauth/dialog' - scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets - api_key: - type: apiKey - name: api_key - in: header - schemas: - Pet: - type: object - required: - - pet_type - properties: - pet_type: - type: string - discriminator: - propertyName: pet_type - mapping: - cachorro: Dog - Cat: - type: object - properties: - name: - type: string - Dog: - type: object - properties: - bark: - type: string - Lizard: - type: object - properties: - lovesRocks: - type: boolean - - MyResponseType: - oneOf: - - $ref: '#/components/schemas/Cat' - - $ref: '#/components/schemas/Dog' - - $ref: '#/components/schemas/Lizard' - discriminator: - propertyName: pet_type - mapping: - dog: '#/components/schemas/Dog' \ No newline at end of file diff --git a/core/src/test/resources/issue-317_1.json b/core/src/test/resources/issue-317_1.json new file mode 100644 index 000000000..ab6f7357b --- /dev/null +++ b/core/src/test/resources/issue-317_1.json @@ -0,0 +1,84 @@ +{ + "openapi":"3.0.0", + "info":{ + "title":"API", + "version":"0.1.0" + }, + "paths":{ + "/resource":{ + "post":{ + "responses":{ + "200":{ + "description":"Created resource", + "content":{ + "application/json":{ + "schema":{ + "type":"string" + } + } + } + } + }, + "summary":"Create resource", + "requestBody":{ + "content":{ + "application/json":{ + "schema":{ + "$ref":"#/components/schemas/Resource" + } + } + }, + "description":"Definition of the resource" + } + } + } + }, + "components":{ + "schemas":{ + "Resource":{ + "type":"object", + "properties":{ + "assignment":{ + "$ref":"#/components/schemas/Foo" + } + } + }, + "Foo":{ + "oneOf":[ + { + "$ref":"#/components/schemas/Foo.Bar" + }, + { + "$ref":"#/components/schemas/Foo.Baz" + } + ], + "discriminator":{ + "propertyName":"type", + "mapping":{ + "Bar":"#/components/schemas/Foo.Bar", + "Baz":"#/components/schemas/Foo.Baz" + } + } + }, + "Foo.Bar":{ + "type":"object", + "properties":{ + "type":{ + "type":"string" + } + } + }, + "Foo.Baz":{ + "type":"object", + "properties":{ + "type":{ + "type":"string" + } + } + } + }, + "securitySchemes":{ + + } + } +} diff --git a/core/src/test/resources/issue-317_2.json b/core/src/test/resources/issue-317_2.json new file mode 100644 index 000000000..b3d35cde4 --- /dev/null +++ b/core/src/test/resources/issue-317_2.json @@ -0,0 +1,91 @@ +{ + "openapi":"3.0.0", + "info":{ + "title":"API", + "version":"0.1.0" + }, + "paths":{ + "/resource":{ + "post":{ + "responses":{ + "200":{ + "description":"Created resource", + "content":{ + "application/json":{ + "schema":{ + "type":"string" + } + } + } + } + }, + "summary":"Create resource", + "requestBody":{ + "content":{ + "application/json":{ + "schema":{ + "$ref":"#/components/schemas/Resource" + } + } + }, + "description":"Definition of the resource" + } + } + } + }, + "components":{ + "schemas":{ + "Resource":{ + "type":"object", + "properties":{ + "assignment":{ + "default":{ + "type":"Bar" + }, + "allOf":[ + { + "$ref":"#/components/schemas/Foo" + } + ] + } + } + }, + "Foo":{ + "oneOf":[ + { + "$ref":"#/components/schemas/Foo.Bar" + }, + { + "$ref":"#/components/schemas/Foo.Baz" + } + ], + "discriminator":{ + "propertyName":"type", + "mapping":{ + "Bar":"#/components/schemas/Foo.Bar", + "Baz":"#/components/schemas/Foo.Baz" + } + } + }, + "Foo.Bar":{ + "type":"object", + "properties":{ + "type":{ + "type":"string" + } + } + }, + "Foo.Baz":{ + "type":"object", + "properties":{ + "type":{ + "type":"string" + } + } + } + }, + "securitySchemes":{ + + } + } +} diff --git a/core/src/test/resources/issue-666-oas-3-0-examples-handling-1.yaml b/core/src/test/resources/issue-666-oas-3-0-examples-handling-1.yaml new file mode 100644 index 000000000..041db37f6 --- /dev/null +++ b/core/src/test/resources/issue-666-oas-3-0-examples-handling-1.yaml @@ -0,0 +1,126 @@ +openapi: 3.0.2 +info: + description: Examples handling + title: Examples + version: 1.0.0 +paths: + /example/request/media-type: + post: + requestBody: + content: + application/json: + example: simple example + schema: + type: string + responses: + '200': + description: description + content: + application/json: + schema: + type: string + /example/response/media-type: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + content: + application/json: + example: simple example + schema: + type: string + /example/parameter/{id}: + post: + parameters: + - name: id + in: path + example: example + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + content: + application/json: + schema: + type: string + /example/response/header: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + headers: + X-Header: + example: example + schema: + type: integer + format: int32 + content: + application/json: + schema: + type: string + /example/model/property: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TestDTO" + responses: + '200': + description: description + content: + application/json: + schema: + type: string + /example/model/property/composed: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TestComposedDTO" + responses: + '200': + description: description + content: + application/json: + schema: + type: string +components: + schemas: + TestDTO: + type: object + properties: + field1: + example: example + type: string + TestDTO2: + type: object + properties: + field2: + example: example + type: string + TestComposedDTO: + type: object + properties: + field1: + type: object + allOf: + - $ref: "#/components/schemas/TestDTO" + - $ref: "#/components/schemas/TestDTO2" \ No newline at end of file diff --git a/core/src/test/resources/issue-666-oas-3-0-examples-handling-2.yaml b/core/src/test/resources/issue-666-oas-3-0-examples-handling-2.yaml new file mode 100644 index 000000000..e0d5914a5 --- /dev/null +++ b/core/src/test/resources/issue-666-oas-3-0-examples-handling-2.yaml @@ -0,0 +1,126 @@ +openapi: 3.0.2 +info: + description: Examples handling + title: Examples + version: 2.0.0 +paths: + /example/request/media-type: + post: + requestBody: + content: + application/json: + example: simple example updated + schema: + type: string + responses: + '200': + description: description + content: + application/json: + schema: + type: string + /example/response/media-type: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + content: + application/json: + example: simple example updated + schema: + type: string + /example/parameter/{id}: + post: + parameters: + - name: id + in: path + example: example updated + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + content: + application/json: + schema: + type: string + /example/response/header: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + headers: + X-Header: + example: updated example + schema: + type: integer + format: int32 + content: + application/json: + schema: + type: string + /example/model/property: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TestDTO" + responses: + '200': + description: description + content: + application/json: + schema: + type: string + /example/model/property/composed: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TestComposedDTO" + responses: + '200': + description: description + content: + application/json: + schema: + type: string +components: + schemas: + TestDTO: + type: object + properties: + field1: + example: example updated + type: string + TestDTO2: + type: object + properties: + field2: + example: example updated + type: string + TestComposedDTO: + type: object + properties: + field1: + type: object + allOf: + - $ref: "#/components/schemas/TestDTO" + - $ref: "#/components/schemas/TestDTO2" \ No newline at end of file diff --git a/core/src/test/resources/issue-666-oas-3-1-examples-handling-1.yaml b/core/src/test/resources/issue-666-oas-3-1-examples-handling-1.yaml new file mode 100644 index 000000000..e03ea0090 --- /dev/null +++ b/core/src/test/resources/issue-666-oas-3-1-examples-handling-1.yaml @@ -0,0 +1,152 @@ +openapi: 3.1.0 +info: + description: Examples handling + title: Examples + version: 1.0.0 +paths: + /example/request/media-type: + post: + requestBody: + content: + application/json: + example: simple example + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /examples/request/media-type: + post: + requestBody: + content: + application/json: + examples: + first: example + second: example + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /example/response/media-type: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + example: simple example + schema: + type: string + /examples/response/media-type: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + examples: + first: example + second: example + schema: + type: string + /example/parameter/{id}: + post: + parameters: + - name: id + in: path + example: example + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /examples/parameter/{id}: + post: + parameters: + - name: id + in: path + examples: + first: example + second: example + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /examples/response/header: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + headers: + X-Header: + examples: + first: example + second: example + schema: + type: integer + format: int32 + content: + application/json: + schema: + type: string + /examples/model/property: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TestDTO" + responses: + '200': + description: description + content: + application/json: + schema: + type: string +components: + schemas: + TestDTO: + type: object + properties: + field1: + examples: + - first example + - second example + type: string \ No newline at end of file diff --git a/core/src/test/resources/issue-666-oas-3-1-examples-handling-2.yaml b/core/src/test/resources/issue-666-oas-3-1-examples-handling-2.yaml new file mode 100644 index 000000000..26d11d91d --- /dev/null +++ b/core/src/test/resources/issue-666-oas-3-1-examples-handling-2.yaml @@ -0,0 +1,152 @@ +openapi: 3.1.0 +info: + description: Examples handling + title: Examples + version: 2.0.0 +paths: + /example/request/media-type: + post: + requestBody: + content: + application/json: + example: simple example updated + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /examples/request/media-type: + post: + requestBody: + content: + application/json: + examples: + first: example updated + second: example updated + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /example/response/media-type: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + example: simple example updated + schema: + type: string + /examples/response/media-type: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + examples: + first: example updated + second: example updated + schema: + type: string + /example/parameter/{id}: + post: + parameters: + - name: id + in: path + example: example updated + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /examples/parameter/{id}: + post: + parameters: + - name: id + in: path + examples: + first: example updated + second: example updated + schema: + type: string + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + content: + application/json: + schema: + type: string + /examples/response/header: + post: + requestBody: + content: + application/json: + schema: + type: string + responses: + '200': + description: description + headers: + X-Header: + examples: + first: example updated + second: example updated + schema: + type: integer + format: int32 + content: + application/json: + schema: + type: string + /examples/model/property: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TestDTO" + responses: + '200': + description: description + content: + application/json: + schema: + type: string +components: + schemas: + TestDTO: + type: object + properties: + field1: + examples: + - first example updated + - second example updated + type: string \ No newline at end of file diff --git a/core/src/test/resources/jsr310_property_1.yaml b/core/src/test/resources/jsr310_property_1.yaml new file mode 100644 index 000000000..7257579ce --- /dev/null +++ b/core/src/test/resources/jsr310_property_1.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.1 +info: + title: Title + version: 1.0.0 + description: Description +paths: + /: + get: + summary: Simple GET + operationId: simpleGet + responses: + default: + description: Default response + content: + application/json: + schema: + properties: + someDateTime: + description: Date time + example: 2021-11-03T13:50:47Z + type: string + format: date-time + description: Simple GET \ No newline at end of file diff --git a/core/src/test/resources/jsr310_property_2.yaml b/core/src/test/resources/jsr310_property_2.yaml new file mode 100644 index 000000000..a51d7abc4 --- /dev/null +++ b/core/src/test/resources/jsr310_property_2.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.1 +info: + title: Title + version: 1.0.0 + description: Description +paths: + /: + get: + summary: Simple GET + operationId: simpleGet + responses: + default: + description: Default response + content: + application/json: + schema: + properties: + someDateTime: + description: Date time + example: 2021-11-03T13:50:47Z + type: string + description: Simple GET \ No newline at end of file diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml new file mode 100644 index 000000000..018a4277c --- /dev/null +++ b/core/src/test/resources/logback-test.xml @@ -0,0 +1,11 @@ +<configuration> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>[%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <root level="debug"> + <appender-ref ref="STDOUT"/> + </root> +</configuration> \ No newline at end of file diff --git a/core/src/test/resources/operationDiff/operation_diff_1.yaml b/core/src/test/resources/operationDiff/operation_diff_1.yaml new file mode 100644 index 000000000..e0faf2df2 --- /dev/null +++ b/core/src/test/resources/operationDiff/operation_diff_1.yaml @@ -0,0 +1,54 @@ +--- +openapi: "3.0.1" +info: + title: "Test title" + description: "This is a test metadata" + termsOfService: "http://test.com" + contact: + name: "Mark Snijder" + url: "marksnijder.nl" + email: "snijderd@gmail.com" + license: + name: "To be decided" + url: "http://test.com" + version: "version 1.0" +paths: + /operation/operation-id: + get: + operationId: operation + responses: + '200': + description: response + content: + application/json: {} + /operation/summary: + get: + summary: summary + responses: + '200': + description: response + content: + application/json: { } + /operation/description: + get: + description: description + responses: + '200': + description: response + content: + application/json: { } + /operation/becomes-deprecated: + get: + responses: + '200': + description: response + content: + application/json: { } + /operation/becomes-not-deprecated: + get: + deprecated: true + responses: + '200': + description: response + content: + application/json: { } diff --git a/core/src/test/resources/operationDiff/operation_diff_2.yaml b/core/src/test/resources/operationDiff/operation_diff_2.yaml new file mode 100644 index 000000000..8f5124955 --- /dev/null +++ b/core/src/test/resources/operationDiff/operation_diff_2.yaml @@ -0,0 +1,55 @@ +--- +openapi: "3.0.1" +info: + title: "Test title" + description: "This is a test metadata" + termsOfService: "http://test.com" + contact: + name: "Mark Snijder" + url: "marksnijder.nl" + email: "snijderd@gmail.com" + license: + name: "To be decided" + url: "http://test.com" + version: "version 1.0" +paths: + /operation/operation-id: + get: + operationId: changed + responses: + '200': + description: response + content: + application/json: {} + /operation/summary: + get: + summary: changed + responses: + '200': + description: response + content: + application/json: { } + /operation/description: + get: + description: changed + responses: + '200': + description: response + content: + application/json: { } + /operation/becomes-deprecated: + get: + deprecated: true + responses: + '200': + description: response + content: + application/json: { } + /operation/becomes-not-deprecated: + get: + deprecated: false + responses: + '200': + description: response + content: + application/json: { } diff --git a/core/src/test/resources/operation_diff_1.yaml b/core/src/test/resources/operation_diff_1.yaml deleted file mode 100644 index 0618b58e6..000000000 --- a/core/src/test/resources/operation_diff_1.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -openapi: "3.0.1" -info: - title: "Test title" - description: "This is a test metadata" - termsOfService: "http://test.com" - contact: - name: "Mark Snijder" - url: "marksnijder.nl" - email: "snijderd@gmail.com" - license: - name: "To be decided" - url: "http://test.com" - version: "version 1.0" -paths: - /pets/{id}: - get: - description: Returns a user based on a single ID, if the user does not have access to the pet - operationId: operation - responses: - '200': - description: response - content: - application/json: {} diff --git a/core/src/test/resources/operation_diff_2.yaml b/core/src/test/resources/operation_diff_2.yaml deleted file mode 100644 index 8d60a2dc1..000000000 --- a/core/src/test/resources/operation_diff_2.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -openapi: "3.0.1" -info: - title: "Test title" - description: "This is a test metadata" - termsOfService: "http://test.com" - contact: - name: "Mark Snijder" - url: "marksnijder.nl" - email: "snijderd@gmail.com" - license: - name: "To be decided" - url: "http://test.com" - version: "version 1.0" -paths: - /pets/{id}: - get: - description: Returns a user based on a single ID, if the user does not have access to the pet - operationId: changed - responses: - '200': - description: response - content: - application/json: {} diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_1.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_1.yaml new file mode 100644 index 000000000..16b769d5b --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_1.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: false + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_10.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_10.yaml new file mode 100644 index 000000000..e704c4f9c --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_10.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_11.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_11.yaml new file mode 100644 index 000000000..ea16b71a9 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_11.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: true + exclusiveMaximum: true + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_12.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_12.yaml new file mode 100644 index 000000000..1c410a34d --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_12.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: true + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_13.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_13.yaml new file mode 100644 index 000000000..896a9c32e --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_13.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: false + exclusiveMaximum: true + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_14.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_14.yaml new file mode 100644 index 000000000..7fa4e1f50 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_14.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: true + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_15.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_15.yaml new file mode 100644 index 000000000..9bd90be6f --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_15.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMaximum: true + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_2.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_2.yaml new file mode 100644 index 000000000..55c1c1983 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_2.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 99 + exclusiveMinimum: false + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_3.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_3.yaml new file mode 100644 index 000000000..f30ef6501 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_3.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 101 + exclusiveMinimum: false + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_4.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_4.yaml new file mode 100644 index 000000000..91cc048c2 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_4.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 9 + maximum: 100 + exclusiveMinimum: false + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_5.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_5.yaml new file mode 100644 index 000000000..4881d7fd9 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_5.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 11 + maximum: 100 + exclusiveMinimum: false + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_6.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_6.yaml new file mode 100644 index 000000000..dbcaa9bed --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_6.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int64 + minimum: 10 + maximum: 100 + exclusiveMinimum: false + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_7.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_7.yaml new file mode 100644 index 000000000..1c410a34d --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_7.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: true + exclusiveMaximum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_8.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_8.yaml new file mode 100644 index 000000000..896a9c32e --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_8.yaml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: false + exclusiveMaximum: true + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_9.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_9.yaml new file mode 100644 index 000000000..8dbf0cd3e --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_9.yaml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + Description + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /int-limits: + get: + summary: get int + description: 'description' + operationId: getInt + parameters: + - name: int + in: query + required: true + schema: + type: integer + format: int32 + minimum: 10 + maximum: 100 + exclusiveMinimum: false + responses: + '200': + description: OK diff --git a/core/src/test/resources/parameterDiff/issue-488-1.json b/core/src/test/resources/parameterDiff/issue-488-1.json new file mode 100644 index 000000000..22e0009dd --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-488-1.json @@ -0,0 +1,98 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Test-API", + "description": "Testdescription", + "version": "v1" + }, + "paths": { + "/api/v1/test": { + "get": { + "tags": [ + "TestTag" + ], + "summary": "Retrieve a list Test objects", + "description": "Usage with testId parameter is recommended.", + "parameters": [ + { + "name": "testId", + "in": "query", + "description": "rename of prop before", + "schema": { + "type": "string" + } + }, + { + "name": "dateFrom", + "in": "query", + "description": "Searches documents with creation date equal or younger specified timestamp", + "schema": { + "type": "string" + } + }, + { + "name": "dateTo", + "in": "query", + "description": "Searches documents with creation date equal or older than specified timestamp", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TestDTO" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + } + }, + "components": { + "schemas": { + "TestDTO": { + "type": "object", + "properties": { + "Id": { + "type": "string", + "nullable": true + }, + "Timestamp": { + "type": "string", + "format": "date-time" + }, + "Test": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + }, + "securitySchemes": { + "BearerAuth": { + "type": "apiKey", + "description": "Fill in your acquired bearer token here, must be like 'Bearer TOKEN_HERE'", + "name": "Authorization", + "in": "header" + } + } + }, + "security": [ + { + "BearerAuth": [ ] + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/parameterDiff/issue-488-2.json b/core/src/test/resources/parameterDiff/issue-488-2.json new file mode 100644 index 000000000..463e210c7 --- /dev/null +++ b/core/src/test/resources/parameterDiff/issue-488-2.json @@ -0,0 +1,98 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Test-API", + "description": "Testdescription", + "version": "v1" + }, + "paths": { + "/api/v1/test": { + "get": { + "tags": [ + "TestTag" + ], + "summary": "Retrieve a list Test objects", + "description": "Usage with testId parameter is recommended.", + "parameters": [ + { + "name": "testIdWillBreak", + "in": "query", + "description": "rename of prop after", + "schema": { + "type": "string" + } + }, + { + "name": "dateFrom", + "in": "query", + "description": "Searches documents with creation date equal or younger specified timestamp", + "schema": { + "type": "string" + } + }, + { + "name": "dateTo", + "in": "query", + "description": "Searches documents with creation date equal or older than specified timestamp", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TestDTO" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + } + } + }, + "components": { + "schemas": { + "TestDTO": { + "type": "object", + "properties": { + "Id": { + "type": "string", + "nullable": true + }, + "Timestamp": { + "type": "string", + "format": "date-time" + }, + "Test": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + }, + "securitySchemes": { + "BearerAuth": { + "type": "apiKey", + "description": "Fill in your acquired bearer token here, must be like 'Bearer TOKEN_HERE'", + "name": "Authorization", + "in": "header" + } + } + }, + "security": [ + { + "BearerAuth": [ ] + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/parameterDiff/parameter_diff_1.yaml b/core/src/test/resources/parameterDiff/parameter_diff_1.yaml new file mode 100644 index 000000000..c17217338 --- /dev/null +++ b/core/src/test/resources/parameterDiff/parameter_diff_1.yaml @@ -0,0 +1,102 @@ +openapi: "3.1.0" +info: + title: "Parameters diff test" + description: "TParameters diff test" + version: "1.0" +paths: + /parameter/added: + get: + responses: + '200': + description: response + content: + application/json: { } + /parameter/removed: + get: + parameters: + - in: query + name: param + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/becomes-deprecated: + get: + parameters: + - in: header + name: X-Header + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/becomes-not-deprecated: + get: + parameters: + - in: header + name: X-Header + deprecated: true + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/required: + get: + parameters: + - in: query + name: param + required: true + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/description: + get: + parameters: + - in: query + name: param + description: description + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/explode: + get: + parameters: + - in: query + name: param + schema: + type: string + explode: true + responses: + '200': + description: response + content: + application/json: { } + /parameter/style: + get: + parameters: + - in: query + name: param + schema: + type: string + style: simple + responses: + '200': + description: response + content: + application/json: { } \ No newline at end of file diff --git a/core/src/test/resources/parameterDiff/parameter_diff_2.yaml b/core/src/test/resources/parameterDiff/parameter_diff_2.yaml new file mode 100644 index 000000000..0c30b6b1d --- /dev/null +++ b/core/src/test/resources/parameterDiff/parameter_diff_2.yaml @@ -0,0 +1,102 @@ +openapi: "3.1.0" +info: + title: "Parameters diff test" + description: "TParameters diff test" + version: "1.0" +paths: + /parameter/added: + get: + parameters: + - in: query + name: param + schema: + type: string + responses: + '200': + description: response + content: + application/json: {} + /parameter/removed: + get: + responses: + '200': + description: response + content: + application/json: { } + /parameter/becomes-deprecated: + get: + parameters: + - in: header + name: X-Header + deprecated: true + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/becomes-not-deprecated: + get: + parameters: + - in: header + name: X-Header + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/required: + get: + parameters: + - in: query + name: param + required: false + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/description: + get: + parameters: + - in: query + name: param + description: changed + schema: + type: string + responses: + '200': + description: response + content: + application/json: { } + /parameter/explode: + get: + parameters: + - in: query + name: param + schema: + type: string + explode: false + responses: + '200': + description: response + content: + application/json: { } + /parameter/style: + get: + parameters: + - in: query + name: param + schema: + type: string + style: matrix + responses: + '200': + description: response + content: + application/json: { } \ No newline at end of file diff --git a/core/src/test/resources/parameterDiff/parameters_overloading.yaml b/core/src/test/resources/parameterDiff/parameters_overloading.yaml new file mode 100644 index 000000000..061444451 --- /dev/null +++ b/core/src/test/resources/parameterDiff/parameters_overloading.yaml @@ -0,0 +1,87 @@ +openapi: 3.0.2 +info: + title: Projects API + version: 1.0.0 +paths: + /projects/{key}: + get: + parameters: + - in: path + name: key + required: true + schema: + type: string + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + /projects/{id}: + get: + parameters: + - in: path + name: id + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + /projects/{uid}: + get: + parameters: + - in: path + name: uid + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + /projects/{id}.{idPostfix}: + get: + parameters: + - in: path + name: id + required: true + schema: + type: string + - in: path + name: idPostfix + required: true + schema: + type: string + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' +components: + schemas: + SampleResponse: + type: object + properties: + id: + type: integer + uid: + type: string + name: + type: string + required: + - id + - uid + - name \ No newline at end of file diff --git a/core/src/test/resources/parameterDiff/parameters_overloading_2.yaml b/core/src/test/resources/parameterDiff/parameters_overloading_2.yaml new file mode 100644 index 000000000..d91b7a0f6 --- /dev/null +++ b/core/src/test/resources/parameterDiff/parameters_overloading_2.yaml @@ -0,0 +1,52 @@ +openapi: 3.0.2 +info: + title: Projects API + version: 1.0.0 +paths: + /projects/{id}: + get: + parameters: + - in: path + name: id + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + /projects/{uid}: + get: + parameters: + - in: path + name: uid + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' +components: + schemas: + SampleResponse: + type: object + properties: + id: + type: integer + uid: + type: string + name: + type: string + required: + - id + - uid + - name \ No newline at end of file diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_new.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_new.yaml new file mode 100644 index 000000000..8faa0730e --- /dev/null +++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_new.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: http://api.example.com/v1 + description: Example api server + +paths: + + /student/{username}: + get: + parameters: + - in: path + name: username # Note the name is the same as in the path + required: true + schema: + type: string + minimum: 1 + + + summary: Returns data about a student + responses: + '200': # status code + description: A JSON Object student data + content: + application/json: + schema: + type: object + items: + $ref: '#/components/schemas/Student' + +components: + schemas: + Student: + type: object + properties: + id: + type: integer + example: "1" + username: + type: string + example: "alloy_horizon" + pattern: A..Z a..z 0..9 . _ - + name: + type: string + example: "Alloy Horizon" diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_old.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_old.yaml new file mode 100644 index 000000000..7ced291d1 --- /dev/null +++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_old.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: http://api.example.com/v1 + description: Example api server + +paths: + + /student/{id}: + get: + parameters: + - in: path + name: id # Note the name is the same as in the path + required: true + schema: + type: string + minimum: 1 + + + summary: Returns data about a student + responses: + '200': # status code + description: A JSON Object student data + content: + application/json: + schema: + type: object + items: + $ref: '#/components/schemas/Student' + +components: + schemas: + Student: + type: object + properties: + id: + type: integer + example: "1" + username: + type: string + example: "alloy_horizon" + pattern: A..Z a..z 0..9 . _ - + name: + type: string + example: "Alloy Horizon" diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_new.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_new.yaml new file mode 100644 index 000000000..8faa0730e --- /dev/null +++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_new.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: http://api.example.com/v1 + description: Example api server + +paths: + + /student/{username}: + get: + parameters: + - in: path + name: username # Note the name is the same as in the path + required: true + schema: + type: string + minimum: 1 + + + summary: Returns data about a student + responses: + '200': # status code + description: A JSON Object student data + content: + application/json: + schema: + type: object + items: + $ref: '#/components/schemas/Student' + +components: + schemas: + Student: + type: object + properties: + id: + type: integer + example: "1" + username: + type: string + example: "alloy_horizon" + pattern: A..Z a..z 0..9 . _ - + name: + type: string + example: "Alloy Horizon" diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_old.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_old.yaml new file mode 100644 index 000000000..96be9b9b2 --- /dev/null +++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_old.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: http://api.example.com/v1 + description: Example api server + +paths: + + /student/{id}: + get: + parameters: + - in: path + name: id # Note the name is the same as in the path + required: true + schema: + type: integer + minimum: 1 + + + summary: Returns data about a student + responses: + '200': # status code + description: A JSON Object student data + content: + application/json: + schema: + type: object + items: + $ref: '#/components/schemas/Student' + +components: + schemas: + Student: + type: object + properties: + id: + type: integer + example: "1" + username: + type: string + example: "alloy_horizon" + pattern: A..Z a..z 0..9 . _ - + name: + type: string + example: "Alloy Horizon" diff --git a/core/src/test/resources/parameters_diff.yaml b/core/src/test/resources/parameters_diff.yaml deleted file mode 100644 index 97cc8e9b9..000000000 --- a/core/src/test/resources/parameters_diff.yaml +++ /dev/null @@ -1,185 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - '/pet/{petId}': - parameters: - - name: newHeaderParam - in: header - required: false - schema: - type: integer - delete: - tags: - - pet - summary: Deletes a pet - description: '' - operationId: deletePet - parameters: - - name: api_key - in: header - required: false - schema: - type: string - - name: newHeaderParam - in: header - required: false - schema: - type: string - - name: petId - in: path - description: Pet id to delete - required: true - schema: - type: integer - format: int64 - responses: - '400': - description: Invalid ID supplied - '404': - description: Pet not found - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - responses: - '405': - description: Invalid input - requestBody: - $ref: '#/components/requestBodies/Pet' - /pet/findByStatus2: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - deprecated: true - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - enum: - - available - - pending - - sold - default: available - responses: - '200': - description: successful operation - content: - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - '400': - description: Invalid status value -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - application/xml: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - schemas: - Tag: - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - xml: - name: Tag - Pet: - type: object - required: - - name - - photoUrls - properties: - id: - type: integer - format: int64 - category: - type: string - name: - type: string - example: doggie - newField: - type: string - example: a field demo - description: a field demo - photoUrls: - type: array - xml: - name: photoUrl - wrapped: true - items: - type: string - tags: - type: array - xml: - name: tag - wrapped: true - items: - $ref: '#/components/schemas/Tag' - status: - type: string - description: pet status in the store - enum: - - available - - pending - - sold - xml: - name: Pet \ No newline at end of file diff --git a/core/src/test/resources/parameters_diff_1.yaml b/core/src/test/resources/parameters_diff_1.yaml deleted file mode 100644 index 97cc8e9b9..000000000 --- a/core/src/test/resources/parameters_diff_1.yaml +++ /dev/null @@ -1,185 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 1.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - '/pet/{petId}': - parameters: - - name: newHeaderParam - in: header - required: false - schema: - type: integer - delete: - tags: - - pet - summary: Deletes a pet - description: '' - operationId: deletePet - parameters: - - name: api_key - in: header - required: false - schema: - type: string - - name: newHeaderParam - in: header - required: false - schema: - type: string - - name: petId - in: path - description: Pet id to delete - required: true - schema: - type: integer - format: int64 - responses: - '400': - description: Invalid ID supplied - '404': - description: Pet not found - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - responses: - '405': - description: Invalid input - requestBody: - $ref: '#/components/requestBodies/Pet' - /pet/findByStatus2: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - deprecated: true - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - enum: - - available - - pending - - sold - default: available - responses: - '200': - description: successful operation - content: - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - '400': - description: Invalid status value -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - application/xml: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - schemas: - Tag: - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - xml: - name: Tag - Pet: - type: object - required: - - name - - photoUrls - properties: - id: - type: integer - format: int64 - category: - type: string - name: - type: string - example: doggie - newField: - type: string - example: a field demo - description: a field demo - photoUrls: - type: array - xml: - name: photoUrl - wrapped: true - items: - type: string - tags: - type: array - xml: - name: tag - wrapped: true - items: - $ref: '#/components/schemas/Tag' - status: - type: string - description: pet status in the store - enum: - - available - - pending - - sold - xml: - name: Pet \ No newline at end of file diff --git a/core/src/test/resources/parameters_diff_2.yaml b/core/src/test/resources/parameters_diff_2.yaml deleted file mode 100644 index a4e38e85d..000000000 --- a/core/src/test/resources/parameters_diff_2.yaml +++ /dev/null @@ -1,183 +0,0 @@ -openapi: 3.0.0 -servers: - - url: 'http://petstore.swagger.io/v2' -info: - description: >- - This is a sample server Petstore server. You can find out more about - Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, - #swagger](http://swagger.io/irc/). For this sample, you can use the api key - `special-key` to test the authorization filters. - version: 2.0.0 - title: Swagger Petstore - termsOfService: 'http://swagger.io/terms/' - contact: - email: apiteam@swagger.io - license: - name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' -tags: - - name: pet - description: Everything about your Pets - externalDocs: - description: Find out more - url: 'http://swagger.io' - - name: store - description: Access to Petstore orders - - name: user - description: Operations about user - externalDocs: - description: Find out more about our store - url: 'http://swagger.io' -paths: - '/pet/{petId}': - delete: - tags: - - pet - summary: Deletes a pet - description: '' - operationId: deletePet - parameters: - - name: api_key - in: header - required: false - schema: - type: string - - name: petId - in: path - description: Pet id to delete - required: true - schema: - type: integer - format: int64 - responses: - '400': - description: Invalid ID supplied - '404': - description: Pet not found - /pet: - post: - tags: - - pet - summary: Add a new pet to the store - description: '' - operationId: addPet - parameters: - - name: tags - in: query - description: add new query param demo - required: true - explode: true - schema: - type: array - items: - type: string - responses: - '405': - description: Invalid input - requestBody: - $ref: '#/components/requestBodies/Pet' - /pet/findByStatus2: - get: - tags: - - pet - summary: Finds Pets by status - description: Multiple status values can be provided with comma separated strings - operationId: findPetsByStatus - parameters: - - name: status - in: query - description: Status values that need to be considered for filter - required: true - explode: true - schema: - type: array - items: - type: string - enum: - - available - - pending - - sold - default: available - responses: - '200': - description: successful operation - content: - application/xml: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Pet' - '400': - description: Invalid status value -externalDocs: - description: Find out more about Swagger - url: 'http://swagger.io' -components: - requestBodies: - Pet: - content: - application/json: - schema: - $ref: '#/components/schemas/Pet' - application/xml: - schema: - $ref: '#/components/schemas/Pet' - description: Pet object that needs to be added to the store - required: true - schemas: - Tag: - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - xml: - name: Tag - Pet: - type: object - required: - - name - - photoUrls - properties: - id: - type: integer - format: int64 - category: - type: string - name: - type: string - example: doggie - newField: - type: string - example: a field demo - description: a field demo - photoUrls: - type: array - xml: - name: photoUrl - wrapped: true - items: - type: string - tags: - type: array - xml: - name: tag - wrapped: true - items: - $ref: '#/components/schemas/Tag' - status: - type: string - description: pet status in the store - enum: - - available - - pending - - sold - xml: - name: Pet \ No newline at end of file diff --git a/core/src/test/resources/path_5.yaml b/core/src/test/resources/path_5.yaml new file mode 100644 index 000000000..9e44371c3 --- /dev/null +++ b/core/src/test/resources/path_5.yaml @@ -0,0 +1,92 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/{petId2}: + post: + tags: + - pet + summary: deletes a pet + description: '' + operationId: deletePet + parameters: + - name: petId2 + in: path + description: Pet ID to delete + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/by_owner-{ownerId}: + get: + tags: + - pet + summary: gets a pet by its owner id + description: '' + operationId: getPetFromOwner + parameters: + - ownerid: ownerId + in: path + description: Owner id of pet that needs to be get + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/by_owner-{ownerId}_name-{petName}: + get: + tags: + - pet + summary: gets a pet by its owner id and its name + description: '' + operationId: getPetFromOwnerAndPetName + parameters: + - ownerid: ownerId + in: path + description: Owner id of pet that needs to be get + required: true + schema: + type: integer + - name: petName + in: path + description: Name of pet that needs to be get + required: true + schema: + type: string + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/path_6.yaml b/core/src/test/resources/path_6.yaml new file mode 100644 index 000000000..9e44371c3 --- /dev/null +++ b/core/src/test/resources/path_6.yaml @@ -0,0 +1,92 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, + #swagger](http://swagger.io/irc/). For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /pet/{petId}: + get: + tags: + - pet + summary: gets a pet by id + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/{petId2}: + post: + tags: + - pet + summary: deletes a pet + description: '' + operationId: deletePet + parameters: + - name: petId2 + in: path + description: Pet ID to delete + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/by_owner-{ownerId}: + get: + tags: + - pet + summary: gets a pet by its owner id + description: '' + operationId: getPetFromOwner + parameters: + - ownerid: ownerId + in: path + description: Owner id of pet that needs to be get + required: true + schema: + type: integer + responses: + '405': + description: Invalid input + /pet/by_owner-{ownerId}_name-{petName}: + get: + tags: + - pet + summary: gets a pet by its owner id and its name + description: '' + operationId: getPetFromOwnerAndPetName + parameters: + - ownerid: ownerId + in: path + description: Owner id of pet that needs to be get + required: true + schema: + type: integer + - name: petName + in: path + description: Name of pet that needs to be get + required: true + schema: + type: string + responses: + '405': + description: Invalid input diff --git a/core/src/test/resources/range_statuscode_1.yaml b/core/src/test/resources/range_statuscode_1.yaml new file mode 100644 index 000000000..4653a70fc --- /dev/null +++ b/core/src/test/resources/range_statuscode_1.yaml @@ -0,0 +1,10 @@ +openapi: 3.0.0 +info: + title: Projects API + version: 1.0.0 +paths: + /pet/: + get: + responses: + "405": + description: "Invalid input" diff --git a/core/src/test/resources/range_statuscode_2.yaml b/core/src/test/resources/range_statuscode_2.yaml new file mode 100644 index 000000000..d0ecbeb75 --- /dev/null +++ b/core/src/test/resources/range_statuscode_2.yaml @@ -0,0 +1,10 @@ +openapi: 3.0.0 +info: + title: Projects API + version: 1.0.0 +paths: + /pet/: + get: + responses: + "4XX": + description: "Invalid input" diff --git a/core/src/test/resources/recursive_model_1.yaml b/core/src/test/resources/recursive_model_1.yaml index 6a05ab0ad..24f545186 100644 --- a/core/src/test/resources/recursive_model_1.yaml +++ b/core/src/test/resources/recursive_model_1.yaml @@ -19,6 +19,7 @@ components: schemas: B: type: object + required: ["message2"] properties: message: type: string @@ -27,4 +28,4 @@ components: details: type: array items: - $ref: '#/components/schemas/B' \ No newline at end of file + $ref: '#/components/schemas/B' diff --git a/core/src/test/resources/recursive_new.yaml b/core/src/test/resources/recursive_new.yaml new file mode 100644 index 000000000..be1688ebc --- /dev/null +++ b/core/src/test/resources/recursive_new.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.1 +info: + title: recursive test + version: "1.0" +servers: + - url: "http://localhost:8000/" +paths: + /ping: + get: + operationId: ping + responses: + "200": + description: OK + content: + text/plain: + schema: + $ref: "#/components/schemas/A" +components: + schemas: + A: + type: object + properties: + propname2: + $ref: "#/components/schemas/B" + B: + type: object + properties: + propname2: + $ref: "#/components/schemas/A" \ No newline at end of file diff --git a/core/src/test/resources/recursive_old.yaml b/core/src/test/resources/recursive_old.yaml new file mode 100644 index 000000000..83788675c --- /dev/null +++ b/core/src/test/resources/recursive_old.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.1 +info: + title: recursive test + version: "1.0" +servers: + - url: "http://localhost:8000/" +paths: + /ping: + get: + operationId: ping + responses: + "200": + description: OK + content: + text/plain: + schema: + $ref: "#/components/schemas/A" +components: + schemas: + A: + type: object + properties: + propname: + $ref: "#/components/schemas/B" + B: + type: object + properties: + propname: + $ref: "#/components/schemas/A" \ No newline at end of file diff --git a/core/src/test/resources/responseHeaderDiff/response_header_1.yaml b/core/src/test/resources/responseHeaderDiff/response_header_1.yaml new file mode 100644 index 000000000..d668deb9a --- /dev/null +++ b/core/src/test/resources/responseHeaderDiff/response_header_1.yaml @@ -0,0 +1,64 @@ +openapi: 3.1.0 +info: + description: Response headers handling + title: response headers + version: 1.0.0 +paths: + /response/headers/deprecated: + get: + responses: + '200': + description: some description + headers: + x-header-becomes-deprecated: + schema: + type: string + x-header-becomes-not-deprecated: + deprecated: true + schema: + type: string + content: + application/json: { } + /response/headers/description: + get: + responses: + '200': + description: some description + headers: + x-header-description-changed: + description: old description + schema: + type: string + x-header-description-added: + schema: + type: string + x-header-description-removed: + description: old description + schema: + type: string + content: + application/json: { } + /response/headers/required: + get: + responses: + '200': + description: some description + headers: + x-header-required-changed: + required: true + schema: + type: string + content: + application/json: { } + /response/headers/explode: + get: + responses: + '200': + description: some description + headers: + x-header-explode-changed: + explode: false + schema: + type: string + content: + application/json: { } \ No newline at end of file diff --git a/core/src/test/resources/responseHeaderDiff/response_header_2.yaml b/core/src/test/resources/responseHeaderDiff/response_header_2.yaml new file mode 100644 index 000000000..fc2175233 --- /dev/null +++ b/core/src/test/resources/responseHeaderDiff/response_header_2.yaml @@ -0,0 +1,64 @@ +openapi: 3.1.0 +info: + description: Response headers handling + title: response headers + version: 1.0.0 +paths: + /response/headers/deprecated: + get: + responses: + '200': + description: some description + headers: + x-header-becomes-deprecated: + deprecated: true + schema: + type: string + x-header-becomes-not-deprecated: + schema: + type: string + content: + application/json: {} + /response/headers/description: + get: + responses: + '200': + description: some description + headers: + x-header-description-changed: + description: new description + schema: + type: string + x-header-description-added: + description: added description + schema: + type: string + x-header-description-removed: + schema: + type: string + content: + application/json: { } + /response/headers/required: + get: + responses: + '200': + description: some description + headers: + x-header-required-changed: + required: false + schema: + type: string + content: + application/json: { } + /response/headers/explode: + get: + responses: + '200': + description: some description + headers: + x-header-explode-changed: + explode: true + schema: + type: string + content: + application/json: { } \ No newline at end of file diff --git a/core/src/test/resources/add-prop-1.yaml b/core/src/test/resources/schemaDiff/schema-add-property-1.yaml similarity index 100% rename from core/src/test/resources/add-prop-1.yaml rename to core/src/test/resources/schemaDiff/schema-add-property-1.yaml diff --git a/core/src/test/resources/add-prop-2.yaml b/core/src/test/resources/schemaDiff/schema-add-property-2.yaml similarity index 100% rename from core/src/test/resources/add-prop-2.yaml rename to core/src/test/resources/schemaDiff/schema-add-property-2.yaml diff --git a/core/src/test/resources/add-prop-put-1.yaml b/core/src/test/resources/schemaDiff/schema-add-property-put-1.yaml similarity index 100% rename from core/src/test/resources/add-prop-put-1.yaml rename to core/src/test/resources/schemaDiff/schema-add-property-put-1.yaml diff --git a/core/src/test/resources/add-prop-put-2.yaml b/core/src/test/resources/schemaDiff/schema-add-property-put-2.yaml similarity index 100% rename from core/src/test/resources/add-prop-put-2.yaml rename to core/src/test/resources/schemaDiff/schema-add-property-put-2.yaml diff --git a/core/src/test/resources/issue-256_1.json b/core/src/test/resources/schemaDiff/schema-additional-properties-1.json similarity index 100% rename from core/src/test/resources/issue-256_1.json rename to core/src/test/resources/schemaDiff/schema-additional-properties-1.json diff --git a/core/src/test/resources/issue-256_2.json b/core/src/test/resources/schemaDiff/schema-additional-properties-2.json similarity index 100% rename from core/src/test/resources/issue-256_2.json rename to core/src/test/resources/schemaDiff/schema-additional-properties-2.json diff --git a/core/src/test/resources/schemaDiff/schema-deprecated-handling-1.yaml b/core/src/test/resources/schemaDiff/schema-deprecated-handling-1.yaml new file mode 100644 index 000000000..48dcd021e --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-deprecated-handling-1.yaml @@ -0,0 +1,33 @@ +openapi: 3.1.0 +info: + description: Schema deprecated handling + title: defaults + version: 1.0.0 +paths: + /schema-diff/deprecated/added: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DeprecatedAdded" + /schema-diff/deprecated/removed: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DeprecatedRemoved" +components: + schemas: + DeprecatedAdded: + type: object + properties: + field1: + type: string + DeprecatedRemoved: + type: object + deprecated: true + properties: + field1: + type: string \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-deprecated-handling-2.yaml b/core/src/test/resources/schemaDiff/schema-deprecated-handling-2.yaml new file mode 100644 index 000000000..00432216c --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-deprecated-handling-2.yaml @@ -0,0 +1,33 @@ +openapi: 3.1.0 +info: + description: Schema deprecated handling + title: defaults + version: 1.0.0 +paths: + /schema-diff/deprecated/added: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DeprecatedAdded" + /schema-diff/deprecated/removed: + post: + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DeprecatedRemoved" +components: + schemas: + DeprecatedAdded: + deprecated: true + type: object + properties: + field1: + type: string + DeprecatedRemoved: + type: object + properties: + field1: + type: string \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml new file mode 100644 index 000000000..23d841484 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml @@ -0,0 +1,42 @@ +openapi: 3.1.0 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/array/items: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: array + items: + type: string + minItems: 1 + maxItems: 100 + field2: + type: array + items: + type: string + minItems: 20 + maxItems: 100 + field3: + type: array + items: + type: string + minItems: 1 + maxItems: 90 + field4: + type: array + items: + type: string + minItems: 1 + maxItems: 100 diff --git a/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml new file mode 100644 index 000000000..0d8e6199b --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml @@ -0,0 +1,42 @@ +openapi: 3.1.0 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/array/items: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: array + items: + type: string + minItems: 2 + maxItems: 100 + field2: + type: array + items: + type: string + minItems: 10 + maxItems: 100 + field3: + type: array + items: + type: string + minItems: 1 + maxItems: 100 + field4: + type: array + items: + type: string + minItems: 1 + maxItems: 90 diff --git a/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-1.yaml new file mode 100644 index 000000000..263d710f7 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-1.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.1 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/object/min-max-properties: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: object + minProperties: 1 + field2: + type: object + minProperties: 10 + field3: + type: object + maxProperties: 10 + field4: + type: object + maxProperties: 100 diff --git a/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-2.yaml new file mode 100644 index 000000000..17183d8a5 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-2.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.1 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/object/min-max-properties: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: object + minProperties: 10 + field2: + type: object + minProperties: 1 + field3: + type: object + maxProperties: 100 + field4: + type: object + maxProperties: 10 diff --git a/core/src/test/resources/schemaDiff/schema-multiple-of-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-1.yaml new file mode 100644 index 000000000..49d3f3b17 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-1.yaml @@ -0,0 +1,29 @@ +openapi: 3.1.0 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/numeric/multiple-of: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: integer + multipleOf: 10 + field2: + type: integer + multipleOf: 0.01 + field3: + type: integer + field4: + type: integer + multipleOf: 10 \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-multiple-of-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-2.yaml new file mode 100644 index 000000000..58311d082 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-2.yaml @@ -0,0 +1,29 @@ +openapi: 3.1.0 +info: + description: Schema diff + title: schema diff + version: 1.0.0 +paths: + /schema/numeric/multiple-of: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + type: integer + multipleOf: 20 + field2: + type: integer + multipleOf: 0.1 + field3: + type: integer + multipleOf: 10 + field4: + type: integer \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-nullable-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-nullable-diff-1.yaml new file mode 100644 index 000000000..b467c3746 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-nullable-diff-1.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.1 +info: + description: Schema diff nullable + title: schema diff nullable + version: 1.0.0 +paths: + /schema/nullable: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field0: + type: integer + field1: + type: integer + nullable: true + field2: + type: integer + nullable: true + field3: + type: integer \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-nullable-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-nullable-diff-2.yaml new file mode 100644 index 000000000..e186f76e6 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-nullable-diff-2.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.1 +info: + description: Schema diff nullable + title: schema diff nullable + version: 1.0.0 +paths: + /schema/nullable: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field0: + type: integer + field1: + type: integer + nullable: false + field2: + type: integer + field3: + type: integer + nullable: true \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-props-defaults-handling-1.yaml b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-1.yaml new file mode 100644 index 000000000..3ecee6886 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-1.yaml @@ -0,0 +1,21 @@ +openapi: 3.1.0 +info: + description: Schema defaults handling + title: defaults + version: 1.0.0 +paths: + /defaults/property-schema/: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + default: default value + type: string \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-props-defaults-handling-2.yaml b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-2.yaml new file mode 100644 index 000000000..143174770 --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-2.yaml @@ -0,0 +1,21 @@ +openapi: 3.1.0 +info: + description: Schema defaults handling + title: defaults + version: 1.0.0 +paths: + /defaults/property-schema/: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field1: + default: default value updated + type: string \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-1.yaml new file mode 100644 index 000000000..dc0e60dec --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-1.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.1 +info: + description: Schema diff uniqueItems + title: schema diff uniqueItems + version: 1.0.0 +paths: + /schema/uniqueItems: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field0: + type: integer + field1: + type: integer + uniqueItems: true + field2: + type: integer + uniqueItems: false + field3: + type: integer + uniqueItems: true + field4: + type: integer \ No newline at end of file diff --git a/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-2.yaml new file mode 100644 index 000000000..12e2b906e --- /dev/null +++ b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-2.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.1 +info: + description: Schema diff uniqueItems + title: schema diff uniqueItems + version: 1.0.0 +paths: + /schema/uniqueItems: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TestDTO' +components: + schemas: + TestDTO: + type: object + properties: + field0: + type: integer + field1: + type: integer + uniqueItems: false + field2: + type: integer + uniqueItems: true + field3: + type: integer + field4: + type: integer + uniqueItems: true \ No newline at end of file diff --git a/maven-example/pom.xml b/maven-example/pom.xml index 7ec2fcc2d..356a10e0e 100644 --- a/maven-example/pom.xml +++ b/maven-example/pom.xml @@ -3,7 +3,7 @@ <parent> <artifactId>openapi-diff-parent</artifactId> <groupId>org.openapitools.openapidiff</groupId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> @@ -23,7 +23,7 @@ <plugin> <groupId>org.openapitools.openapidiff</groupId> <artifactId>openapi-diff-maven</artifactId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> <executions> <execution> <goals> @@ -33,6 +33,15 @@ <oldSpec>${project.basedir}/../maven/src/test/resources/oldspec.yaml</oldSpec> <newSpec>${project.basedir}/../maven/src/test/resources/newspec.yaml</newSpec> <failOnIncompatible>true</failOnIncompatible> + <consoleOutputFileName>${project.basedir}/../maven/target/diff.txt</consoleOutputFileName> + <jsonOutputFileName>${project.basedir}/../maven/target/diff.json</jsonOutputFileName> + <markdownOutputFileName>${project.basedir}/../maven/target/diff.md</markdownOutputFileName> + <configFiles> + <configFile>${project.basedir}/../maven/src/test/resources/test-config-file.yaml</configFile> + </configFiles> + <configProps> + <incompatible.request.params.required.increased>false</incompatible.request.params.required.increased> + </configProps> </configuration> </execution> </executions> diff --git a/maven/pom.xml b/maven/pom.xml index ed52e83c0..afaa97de4 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>org.openapitools.openapidiff</groupId> <artifactId>openapi-diff-parent</artifactId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> </parent> <artifactId>openapi-diff-maven</artifactId> @@ -43,6 +43,11 @@ <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -51,12 +56,12 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> - <version>3.6.2</version> + <version>3.8.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> - <version>3.9.1</version> + <version>3.21.0</version> </plugin> </plugins> </pluginManagement> diff --git a/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java b/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java index 900410420..4fd7d9e14 100644 --- a/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java +++ b/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java @@ -1,5 +1,14 @@ package org.openapitools.openapidiff.maven; +import static org.openapitools.openapidiff.core.utils.FileUtils.writeToFile; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.Map; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -7,12 +16,17 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.openapitools.openapidiff.core.OpenApiCompare; +import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions; import org.openapitools.openapidiff.core.model.ChangedOpenApi; +import org.openapitools.openapidiff.core.output.AsciidocRender; import org.openapitools.openapidiff.core.output.ConsoleRender; +import org.openapitools.openapidiff.core.output.JsonRender; +import org.openapitools.openapidiff.core.output.MarkdownRender; /** A Maven Mojo that diffs two OpenAPI specifications and reports on differences. */ @Mojo(name = "diff", defaultPhase = LifecyclePhase.TEST) public class OpenApiDiffMojo extends AbstractMojo { + @Parameter(property = "oldSpec") String oldSpec; @@ -25,11 +39,58 @@ public class OpenApiDiffMojo extends AbstractMojo { @Parameter(property = "failOnChanged", defaultValue = "false") Boolean failOnChanged = false; + @Parameter(property = "skip", defaultValue = "false") + Boolean skip = false; + + @Parameter(property = "consoleOutputFileName") + String consoleOutputFileName; + + @Parameter(property = "jsonOutputFileName") + String jsonOutputFileName; + + @Parameter(property = "markdownOutputFileName") + String markdownOutputFileName; + + @Parameter(property = "configFiles") + List<File> configFiles; + + @Parameter(property = "configProps") + Map<String, String> configProps; + + @Parameter(property = "asciidocOutputFileName") + String asciidocOutputFileName; + @Override public void execute() throws MojoExecutionException, MojoFailureException { + if (Boolean.TRUE.equals(skip)) { + getLog().info("Skipping openapi-diff execution"); + return; + } + try { - final ChangedOpenApi diff = OpenApiCompare.fromLocations(oldSpec, newSpec); - getLog().info(new ConsoleRender().render(diff)); + OpenApiDiffOptions.Builder optionBuilder = OpenApiDiffOptions.builder(); + if (configFiles != null) { + configFiles.forEach(optionBuilder::configYaml); + } + if (configProps != null) { + configProps.forEach(optionBuilder::configProperty); + } + OpenApiDiffOptions options = optionBuilder.build(); + + final ChangedOpenApi diff = OpenApiCompare.fromLocations(oldSpec, newSpec, null, options); + + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) { + new ConsoleRender().render(diff, outputStreamWriter); + getLog().info(outputStream.toString()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + writeDiffAsTextToFile(diff); + writeDiffAsJsonToFile(diff); + writeDiffAsMarkdownToFile(diff); + writeDiffAsAsciidocToFile(diff); if (failOnIncompatible && diff.isIncompatible()) { throw new BackwardIncompatibilityException("The API changes broke backward compatibility"); @@ -42,4 +103,20 @@ public void execute() throws MojoExecutionException, MojoFailureException { throw new MojoExecutionException("Unexpected error", e); } } + + private void writeDiffAsTextToFile(final ChangedOpenApi diff) { + writeToFile(new ConsoleRender(), diff, consoleOutputFileName); + } + + private void writeDiffAsJsonToFile(final ChangedOpenApi diff) { + writeToFile(new JsonRender(), diff, jsonOutputFileName); + } + + private void writeDiffAsMarkdownToFile(final ChangedOpenApi diff) { + writeToFile(new MarkdownRender(), diff, markdownOutputFileName); + } + + private void writeDiffAsAsciidocToFile(final ChangedOpenApi diff) { + writeToFile(new AsciidocRender(), diff, asciidocOutputFileName); + } } diff --git a/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java b/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java index 5ecc457e5..d38c66bde 100644 --- a/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java +++ b/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java @@ -1,15 +1,47 @@ package org.openapitools.openapidiff.maven; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; import org.apache.maven.plugin.MojoExecutionException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class OpenApiDiffMojoTest { + + private static final Logger logger = LoggerFactory.getLogger(OpenApiDiffMojoTest.class); + + private final File oldSpecFile = new File("src/test/resources/oldspec.yaml"); + private final File newSpecFile = new File("src/test/resources/newspec.yaml"); + private final File configFile = new File("src/test/resources/test-config-file.yaml"); + + private final File consoleOutputfile = new File("target/diff.txt"); + private final File markdownOutputfile = new File("target/diff.md"); + private final File jsonOutputfile = new File("target/diff.json"); + private final File asciidocOutputfile = new File("target/diff.adoc"); + + @BeforeEach + void setup() { + cleanupGeneratedFiles(); + } + + @AfterEach + void tearDown() { + cleanupGeneratedFiles(); + } + @Test void Should_NotThrow_When_SpecHasNoChanges() { - final String oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath(); + final String oldSpec = oldSpecFile.getAbsolutePath(); final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); mojo.oldSpec = oldSpec; @@ -22,8 +54,8 @@ void Should_NotThrow_When_SpecHasNoChanges() { @Test void Should_NotThrow_When_SpecIsCompatible() { final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); - mojo.oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath(); - mojo.newSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); mojo.failOnIncompatible = true; assertDoesNotThrow(mojo::execute); @@ -32,8 +64,8 @@ void Should_NotThrow_When_SpecIsCompatible() { @Test void Should_Throw_When_SpecIsDifferent() { final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); - mojo.oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath(); - mojo.newSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); mojo.failOnChanged = true; assertThrows(ApiChangedException.class, mojo::execute); @@ -43,7 +75,7 @@ void Should_Throw_When_SpecIsDifferent() { void Should_MojoExecutionException_When_MissingOldSpec() { final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); mojo.oldSpec = new File("DOES_NOT_EXIST").getAbsolutePath(); - mojo.newSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); assertThrows(MojoExecutionException.class, mojo::execute); } @@ -51,7 +83,7 @@ void Should_MojoExecutionException_When_MissingOldSpec() { @Test void Should_MojoExecutionException_When_MissingNewSpec() { final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); - mojo.oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); mojo.newSpec = new File("DOES_NOT_EXIST").getAbsolutePath(); assertThrows(MojoExecutionException.class, mojo::execute); @@ -60,8 +92,8 @@ void Should_MojoExecutionException_When_MissingNewSpec() { @Test void Should_NotThrow_When_DefaultsAndSpecIsIncompatible() { final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); - mojo.oldSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath(); - mojo.newSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath(); + mojo.oldSpec = newSpecFile.getAbsolutePath(); + mojo.newSpec = oldSpecFile.getAbsolutePath(); assertDoesNotThrow(mojo::execute); } @@ -69,10 +101,110 @@ void Should_NotThrow_When_DefaultsAndSpecIsIncompatible() { @Test void Should_BackwardIncompatibilityException_When_WantsExceptionAndSpecIsIncompatible() { final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); - mojo.oldSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath(); - mojo.newSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath(); + mojo.oldSpec = newSpecFile.getAbsolutePath(); + mojo.newSpec = oldSpecFile.getAbsolutePath(); mojo.failOnIncompatible = true; assertThrows(BackwardIncompatibilityException.class, mojo::execute); } + + @Test + void Should_NotThrow_When_ConfigWithIncompatibleChecksDisabled() { + final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); + mojo.oldSpec = newSpecFile.getAbsolutePath(); + mojo.newSpec = oldSpecFile.getAbsolutePath(); + mojo.failOnIncompatible = true; + mojo.configFiles = Collections.singletonList(configFile); + mojo.configProps = + Collections.singletonMap("incompatible.request.params.required.increased", "false"); + + assertDoesNotThrow(mojo::execute); + } + + @Test + void Should_Skip_Mojo_WhenSkipIsTrue() { + final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); + mojo.oldSpec = newSpecFile.getAbsolutePath(); + mojo.newSpec = oldSpecFile.getAbsolutePath(); + mojo.failOnIncompatible = true; + mojo.skip = true; + + assertDoesNotThrow(mojo::execute); + } + + @Test + void Should_outputToTextFile_When_SpecIsDifferent() { + final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); + + mojo.consoleOutputFileName = consoleOutputfile.getAbsolutePath(); + mojo.failOnChanged = true; + + assertThrows(ApiChangedException.class, mojo::execute); + + assertTrue(Files.exists(consoleOutputfile.toPath())); + } + + @Test + void Should_outputToMarkdownFile_When_SpecIsDifferent() { + final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); + + mojo.markdownOutputFileName = markdownOutputfile.getAbsolutePath(); + mojo.failOnChanged = true; + + assertThrows(ApiChangedException.class, mojo::execute); + + assertTrue(Files.exists(markdownOutputfile.toPath())); + } + + @Test + void Should_outputToJsonFile_When_SpecIsDifferent() { + final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); + + mojo.jsonOutputFileName = jsonOutputfile.getAbsolutePath(); + mojo.failOnChanged = true; + + assertThrows(ApiChangedException.class, mojo::execute); + + assertTrue(Files.exists(jsonOutputfile.toPath())); + } + + @Test + void Should_outputToAsccidocFile_When_SpecIsDifferent() { + final OpenApiDiffMojo mojo = new OpenApiDiffMojo(); + mojo.oldSpec = oldSpecFile.getAbsolutePath(); + mojo.newSpec = newSpecFile.getAbsolutePath(); + + mojo.asciidocOutputFileName = asciidocOutputfile.getAbsolutePath(); + mojo.failOnChanged = true; + + assertThrows(ApiChangedException.class, mojo::execute); + + assertTrue(Files.exists(asciidocOutputfile.toPath())); + } + + private void cleanupGeneratedFiles() { + try { + Files.deleteIfExists(Paths.get(consoleOutputfile.getPath())); + } catch (IOException ioException) { + logger.warn("Exception while trying to delete file {}", consoleOutputfile.getAbsolutePath()); + } + + try { + Files.deleteIfExists(Paths.get(markdownOutputfile.getPath())); + } catch (IOException ioException) { + logger.warn("Exception while trying to delete file {}", markdownOutputfile.getAbsolutePath()); + } + + try { + Files.deleteIfExists(Paths.get(jsonOutputfile.getPath())); + } catch (IOException ioException) { + logger.warn("Exception while trying to delete file {}", jsonOutputfile.getAbsolutePath()); + } + } } diff --git a/maven/src/test/resources/logback.xml b/maven/src/test/resources/logback.xml new file mode 100644 index 000000000..018a4277c --- /dev/null +++ b/maven/src/test/resources/logback.xml @@ -0,0 +1,11 @@ +<configuration> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>[%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <root level="debug"> + <appender-ref ref="STDOUT"/> + </root> +</configuration> \ No newline at end of file diff --git a/maven/src/test/resources/newspec.yaml b/maven/src/test/resources/newspec.yaml index e95978f04..7d326b882 100644 --- a/maven/src/test/resources/newspec.yaml +++ b/maven/src/test/resources/newspec.yaml @@ -1,15 +1,33 @@ ---- -openapi: 3.0.3 +openapi: 3.0.0 info: - title: Generated API - version: "1.0" + description: myDesc + title: myTitle + version: 1.0.0 paths: - /hello: + /widgets: get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + schema: + type: string + - name: query-param-2 + in: query + schema: + type: string responses: - "200": - description: OK + '200': + description: successful operation content: - text/plain: + application/json: schema: - type: string + type: object + properties: + prop1: + type: string + prop2: + type: string + required: + - prop1 + - prop2 diff --git a/maven/src/test/resources/oldspec.yaml b/maven/src/test/resources/oldspec.yaml index 2f2735763..40cd4175e 100644 --- a/maven/src/test/resources/oldspec.yaml +++ b/maven/src/test/resources/oldspec.yaml @@ -1,6 +1,33 @@ ---- -openapi: 3.0.3 +openapi: 3.0.0 info: - title: Generated API - version: "1.0" -paths: {} + description: myDesc + title: myTitle + version: 1.0.0 +paths: + /widgets: + get: + operationId: listWidgets + parameters: + - name: query-param-1 + in: query + required: true + schema: + type: string + - name: query-param-2 + in: query + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + prop1: + type: string + prop2: + type: string + required: + - prop1 diff --git a/maven/src/test/resources/test-config-file.yaml b/maven/src/test/resources/test-config-file.yaml new file mode 100644 index 000000000..633ad0be3 --- /dev/null +++ b/maven/src/test/resources/test-config-file.yaml @@ -0,0 +1,8 @@ +#### +## Config file for openapi-diff +#### + +incompatible: + response: + required: + decreased: false diff --git a/mvnw b/mvnw index fc7efd17d..b7f064624 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Apache Maven Wrapper startup batch script, version 3.1.1 # # Required ENV vars: # ------------------ @@ -27,7 +27,6 @@ # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -36,6 +35,10 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi @@ -54,38 +57,16 @@ case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true - # - # Look for the Apple JDKs first to preserve the existing behaviour, and then look - # for the new JDKs provided by Oracle. - # - if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then - # - # Apple JDKs - # - export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then - # - # Apple JDKs - # - export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then - # - # Oracle JDKs - # - export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home - fi - - if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then - # - # Apple JDKs - # - export JAVA_HOME=`/usr/libexec/java_home` - fi - ;; + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; esac if [ -z "$JAVA_HOME" ] ; then @@ -94,49 +75,18 @@ if [ -z "$JAVA_HOME" ] ; then fi fi -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi -# For Migwn, ensure paths are in UNIX format before anything is touched +# For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then @@ -168,7 +118,7 @@ if [ -z "$JAVACMD" ] ; then JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="`which java`" + JAVACMD="`\\unset -f command; \\command -v java`" fi fi @@ -182,31 +132,29 @@ if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` -fi - # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - local basedir=$(pwd) - local wdir=$(pwd) + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi - wdir=$(cd "$wdir/.."; pwd) + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround done - echo "${basedir}" + printf '%s' "$(cd "$basedir"; pwd)" } # concatenates all lines of a file @@ -216,10 +164,115 @@ concat_lines() { fi } -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +BASE_DIR=$(find_maven_basedir "$(dirname $0)") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $wrapperUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + QUIET="--quiet" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + elif command -v curl > /dev/null; then + QUIET="--silent" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=`cygpath --path --windows "$javaSource"` + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" -# Provide a "standardized" way to retrieve the CLI args that will +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" export MAVEN_CMD_LINE_ARGS @@ -228,7 +281,7 @@ WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS - + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 001048081..474c9d6b7 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,15 +18,14 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script +@REM Apache Maven Wrapper startup batch script, version 3.1.1 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -35,7 +34,9 @@ @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME @@ -44,8 +45,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal @@ -80,8 +81,6 @@ goto error :init -set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* - @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. @@ -117,11 +116,54 @@ for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do s :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end @@ -131,15 +173,15 @@ set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause +if "%MAVEN_BATCH_PAUSE%"=="on" pause -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% -exit /B %ERROR_CODE% +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 06f6bfd4b..04acfd2f5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ <groupId>org.openapitools.openapidiff</groupId> <artifactId>openapi-diff-parent</artifactId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>openapi-diff-parent</name> @@ -75,7 +75,7 @@ <maven.compiler.target>1.8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> - <project.build.outputTimestamp>2017-12-12T00:39:30Z</project.build.outputTimestamp> + <project.build.outputTimestamp>2025-01-27T23:01:35Z</project.build.outputTimestamp> <project.scm.id>github</project.scm.id> <sonar.organization>openapitools</sonar.organization> @@ -83,8 +83,8 @@ <sonar.host.url>https://sonarcloud.io</sonar.host.url> <sonar.moduleKey>${project.artifactId}</sonar.moduleKey> - <swagger-parser.version>2.0.28</swagger-parser.version> - <slf4j.version>1.7.32</slf4j.version> + <swagger-parser.version>2.1.26</swagger-parser.version> + <slf4j.version>2.0.17</slf4j.version> </properties> <dependencyManagement> @@ -92,12 +92,12 @@ <dependency> <groupId>org.openapitools.openapidiff</groupId> <artifactId>openapi-diff-core</artifactId> - <version>2.0.1-SNAPSHOT</version> + <version>2.1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> - <version>5.8.2</version> + <version>5.12.1</version> <type>pom</type> <scope>import</scope> </dependency> @@ -119,22 +119,27 @@ <dependency> <groupId>com.j2html</groupId> <artifactId>j2html</artifactId> - <version>1.5.0</version> + <version>1.6.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.4</version> </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-configuration2</artifactId> + <version>2.11.0</version> + </dependency> <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> - <version>1.5.0</version> + <version>1.9.0</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> - <version>3.12.0</version> + <version>3.17.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> @@ -143,24 +148,23 @@ </dependency> <dependency> <groupId>org.slf4j</groupId> - <artifactId>slf4j-log4j12</artifactId> + <artifactId>jcl-over-slf4j</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> - <groupId>commons-httpclient</groupId> - <artifactId>commons-httpclient</artifactId> - <version>3.1</version> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>${slf4j.version}</version> </dependency> <dependency> - <groupId>org.assertj</groupId> - <artifactId>assertj-core</artifactId> - <version>3.21.0</version> - <scope>test</scope> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.3.14</version> </dependency> <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-simple</artifactId> - <version>${slf4j.version}</version> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.27.3</version> <scope>test</scope> </dependency> </dependencies> @@ -252,6 +256,18 @@ </execution> </executions> </plugin> + <plugin> + <groupId>org.cyclonedx</groupId> + <artifactId>cyclonedx-maven-plugin</artifactId> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>makeAggregateBom</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> </profile> @@ -262,32 +278,39 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.8.1</version> + <version>3.14.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> - <version>3.2.1</version> + <version>3.3.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>3.2.0</version> + <version>3.4.2</version> + <configuration> + <archive> + <manifestEntries> + <Implementation-Version>${project.version}</Implementation-Version> + </manifestEntries> + </archive> + </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> - <version>2.5.3</version> + <version>3.1.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>3.3.1</version> + <version>3.11.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>2.22.2</version> + <version>3.5.3</version> </plugin> <plugin> <groupId>com.coveo</groupId> @@ -302,33 +325,38 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> - <version>3.0.1</version> + <version>3.2.7</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> - <version>2.8.2</version> + <version>3.1.4</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> - <version>3.2.4</version> + <version>3.6.0</version> </plugin> <plugin> <groupId>org.sonatype.plugins</groupId> <artifactId>nexus-staging-maven-plugin</artifactId> - <version>1.6.8</version> + <version>1.7.0</version> <extensions>true</extensions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> - <version>0.8.7</version> + <version>0.8.13</version> </plugin> <plugin> <groupId>org.sonarsource.scanner.maven</groupId> <artifactId>sonar-maven-plugin</artifactId> - <version>3.9.1.2184</version> + <version>5.1.0.4751</version> + </plugin> + <plugin> + <groupId>org.cyclonedx</groupId> + <artifactId>cyclonedx-maven-plugin</artifactId> + <version>2.9.1</version> </plugin> </plugins> </pluginManagement> @@ -365,7 +393,7 @@ <skipRepositoryCheck>true</skipRepositoryCheck> <hooks> <pre-commit> - mvn com.coveo:fmt-maven-plugin:format + ./mvnw com.coveo:fmt-maven-plugin:format </pre-commit> </hooks> </configuration>