├── config └── checkstyle │ └── suppressions.xml ├── swagger-brake-integration-tests ├── src │ └── test │ │ ├── resources │ │ ├── example-projects │ │ │ ├── swagger-brake-example │ │ │ │ ├── src │ │ │ │ │ └── main │ │ │ │ │ │ └── webapp │ │ │ │ │ │ └── WEB-INF │ │ │ │ │ │ └── web.xml │ │ │ │ ├── .mvn │ │ │ │ │ └── wrapper │ │ │ │ │ │ └── maven-wrapper.properties │ │ │ │ └── Dockerfile │ │ │ ├── swagger-brake-gradle-example │ │ │ │ ├── src │ │ │ │ │ └── main │ │ │ │ │ │ └── webapp │ │ │ │ │ │ └── WEB-INF │ │ │ │ │ │ └── web.xml │ │ │ │ ├── gradle.properties │ │ │ │ ├── settings.gradle │ │ │ │ ├── Dockerfile │ │ │ │ └── gradle │ │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ └── swagger-brake-maven-example │ │ │ │ ├── src │ │ │ │ └── main │ │ │ │ │ └── webapp │ │ │ │ │ └── WEB-INF │ │ │ │ │ └── web.xml │ │ │ │ ├── .mvn │ │ │ │ └── wrapper │ │ │ │ │ └── maven-wrapper.properties │ │ │ │ ├── Dockerfile │ │ │ │ └── entrypoint.sh │ │ ├── testng.xml │ │ └── logback.xml │ │ └── java │ │ └── com │ │ └── docktape │ │ └── swagger │ │ └── brake │ │ └── integration │ │ ├── project │ │ ├── BuildFailureException.java │ │ ├── PackagingType.java │ │ ├── cli │ │ │ └── CliProjectParameter.java │ │ ├── ProjectParameter.java │ │ └── plugin │ │ │ ├── PluginProjectParameter.java │ │ │ ├── PluginProjectContainerBase.java │ │ │ └── ExampleMavenProjectContainer.java │ │ ├── support │ │ ├── ArtifactIdGenerator.java │ │ ├── SwaggerExamples.java │ │ ├── SwaggerBrakeVersion.java │ │ └── ClasspathFileLoader.java │ │ ├── docker │ │ └── NetworkHolder.java │ │ └── artifactory │ │ ├── page │ │ ├── ArtifactoryRepositoriesPage.java │ │ ├── ArtifactoryLoginPage.java │ │ └── ArtifactoryMainPage.java │ │ ├── ArtifactoryServerPool.java │ │ └── factory │ │ └── PageFactory.java └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src-docs ├── src │ ├── .vuepress │ │ ├── enhanceApp.js │ │ ├── styles │ │ │ ├── index.styl │ │ │ └── palette.styl │ │ └── config.js │ └── troubleshooting │ │ └── README.md ├── .npmignore └── package.json ├── settings.gradle ├── swagger-brake ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── docktape │ │ │ └── swagger │ │ │ └── brake │ │ │ ├── runner │ │ │ ├── OutputFormat.java │ │ │ ├── RunnerConfiguration.java │ │ │ ├── exception │ │ │ │ └── LatestArtifactDownloadException.java │ │ │ ├── ArtifactPackaging.java │ │ │ ├── openapi │ │ │ │ ├── ApiInfoFactory.java │ │ │ │ └── OpenApiFactory.java │ │ │ ├── Options.java │ │ │ ├── download │ │ │ │ └── DownloadOptionsFactory.java │ │ │ └── CheckerOptionsFactory.java │ │ │ ├── core │ │ │ ├── model │ │ │ │ ├── service │ │ │ │ │ ├── Resolver.java │ │ │ │ │ ├── RequestParameterInTypeResolver.java │ │ │ │ │ └── TypeRefNameResolver.java │ │ │ │ ├── transformer │ │ │ │ │ ├── Transformer.java │ │ │ │ │ ├── MediaTypeTransformer.java │ │ │ │ │ ├── PathDetail.java │ │ │ │ │ └── RequestBodyTransformer.java │ │ │ │ ├── HttpMethod.java │ │ │ │ ├── store │ │ │ │ │ ├── ParameterStore.java │ │ │ │ │ ├── ResponseStore.java │ │ │ │ │ ├── ResponsesTransformer.java │ │ │ │ │ ├── ParametersTransformer.java │ │ │ │ │ ├── ComponentsTransformer.java │ │ │ │ │ ├── SchemaStore.java │ │ │ │ │ └── StoreProvider.java │ │ │ │ ├── MediaType.java │ │ │ │ ├── Specification.java │ │ │ │ ├── SchemaAttribute.java │ │ │ │ ├── RequestParameterInType.java │ │ │ │ ├── StringSchema.java │ │ │ │ ├── parameter │ │ │ │ │ ├── RequestParameter.java │ │ │ │ │ └── StringRequestParameter.java │ │ │ │ ├── ArraySchema.java │ │ │ │ ├── Path.java │ │ │ │ ├── Request.java │ │ │ │ ├── Response.java │ │ │ │ └── NumberSchema.java │ │ │ ├── BreakingChange.java │ │ │ ├── rule │ │ │ │ ├── request │ │ │ │ │ ├── parameter │ │ │ │ │ │ └── constraint │ │ │ │ │ │ │ ├── ConstrainedValue.java │ │ │ │ │ │ │ ├── NoConstrainedValue.java │ │ │ │ │ │ │ ├── Constraint.java │ │ │ │ │ │ │ ├── ConstraintChange.java │ │ │ │ │ │ │ ├── StringConstrainedValue.java │ │ │ │ │ │ │ ├── ArrayConstrainedValue.java │ │ │ │ │ │ │ ├── number │ │ │ │ │ │ │ └── PrettyFormattedBigDecimal.java │ │ │ │ │ │ │ ├── NumberConstrainedValue.java │ │ │ │ │ │ │ └── RequestParameterConstraintChangedBreakingChange.java │ │ │ │ │ ├── RequestParameterDeletedBreakingChange.java │ │ │ │ │ ├── RequestParameterRequiredBreakingChange.java │ │ │ │ │ ├── RequestTypeAttributeRemovedBreakingChange.java │ │ │ │ │ ├── RequestTypeEnumValueDeletedBreakingChange.java │ │ │ │ │ ├── RequestMediaTypeDeletedBreakingChange.java │ │ │ │ │ ├── RequestParameterEnumValueDeletedBreakingChange.java │ │ │ │ │ ├── RequestTypeChangedBreakingChange.java │ │ │ │ │ ├── RequestParameterInTypeChangedBreakingChange.java │ │ │ │ │ └── RequestParameterTypeChangedBreakingChange.java │ │ │ │ ├── BreakingChangeRule.java │ │ │ │ ├── path │ │ │ │ │ └── PathDeletedBreakingChange.java │ │ │ │ ├── beta │ │ │ │ │ └── StandardApiToBetaApiBreakingChange.java │ │ │ │ ├── response │ │ │ │ │ ├── ResponseDeletedBreakingChange.java │ │ │ │ │ ├── ResponseTypeEnumValueDeletedBreakingChange.java │ │ │ │ │ ├── ResponseTypeAttributeRemovedBreakingChange.java │ │ │ │ │ ├── ResponseMediaTypeDeletedBreakingChange.java │ │ │ │ │ └── ResponseTypeChangedBreakingChange.java │ │ │ │ └── PathSkipper.java │ │ │ ├── ApiInfo.java │ │ │ ├── CoreConfiguration.java │ │ │ ├── BreakChecker.java │ │ │ ├── CheckerOptions.java │ │ │ ├── util │ │ │ │ ├── BigDecimalComparator.java │ │ │ │ ├── DecimalFormatter.java │ │ │ │ └── PathNormalizer.java │ │ │ ├── CheckerOptionsProvider.java │ │ │ └── DefaultBreakChecker.java │ │ │ ├── maven │ │ │ ├── LatestArtifactDownloader.java │ │ │ ├── http │ │ │ │ ├── UnauthorizedException.java │ │ │ │ ├── Base64Utils.java │ │ │ │ └── HttpClientErrorHandler.java │ │ │ ├── jar │ │ │ │ ├── filename │ │ │ │ │ ├── ApiFileNameChecker.java │ │ │ │ │ ├── SwaggerApiFileNameChecker.java │ │ │ │ │ ├── ApiFileNameCheckerChain.java │ │ │ │ │ ├── ConfigurableApiFileNameChecker.java │ │ │ │ │ └── ApiFilenameCheckerFactory.java │ │ │ │ ├── ApiFileResolverParameter.java │ │ │ │ └── JarScanner.java │ │ │ ├── maven2 │ │ │ │ ├── ArtifactVersionDecider.java │ │ │ │ ├── LatestJarArtifactDownloader.java │ │ │ │ ├── RepositoryRequestFactory.java │ │ │ │ ├── Maven2LatestArtifactDownloader.java │ │ │ │ ├── TemporaryJarFileDownloader.java │ │ │ │ ├── MavenMetadataDownloader.java │ │ │ │ └── LatestArtifactNameResolver.java │ │ │ ├── model │ │ │ │ ├── MavenSnapshot.java │ │ │ │ ├── MavenMetadata.java │ │ │ │ └── MavenVersioning.java │ │ │ ├── DownloadOptions.java │ │ │ ├── LatestArtifactDownloaderFactory.java │ │ │ └── MavenConfiguration.java │ │ │ └── report │ │ │ ├── CheckableReporter.java │ │ │ ├── html │ │ │ ├── HtmlData.java │ │ │ ├── BreakingChangeTableRow.java │ │ │ └── MustacheContentResolver.java │ │ │ ├── file │ │ │ ├── DirectoryCreator.java │ │ │ └── FileWriter.java │ │ │ ├── ReporterConfiguration.java │ │ │ ├── Reporter.java │ │ │ ├── CompositeReporter.java │ │ │ └── json │ │ │ └── JsonConverter.java │ └── test │ │ ├── resources │ │ └── swaggers │ │ │ ├── v2 │ │ │ ├── path │ │ │ │ └── deleted │ │ │ │ │ └── deprecated │ │ │ │ │ ├── petstore_v2.yaml │ │ │ │ │ └── petstore.yaml │ │ │ ├── validation │ │ │ │ └── requestparam │ │ │ │ │ ├── path_swagger.json │ │ │ │ │ ├── query_swagger.json │ │ │ │ │ └── header_swagger.json │ │ │ ├── nobreakingchange │ │ │ │ ├── refrequestbody │ │ │ │ │ ├── schema.yaml │ │ │ │ │ └── schema_v2.yaml │ │ │ │ └── recursive2 │ │ │ │ │ └── swagger.json │ │ │ └── request │ │ │ │ └── optionalparameteradded │ │ │ │ └── swagger_v1.yaml │ │ │ └── v3 │ │ │ ├── response │ │ │ ├── attributeremoved │ │ │ │ ├── petstore_v2.yaml │ │ │ │ └── petstore.yaml │ │ │ └── attributeremoved-deprecated │ │ │ │ ├── petstore_v2.yaml │ │ │ │ └── petstore.yaml │ │ │ └── request │ │ │ ├── attributeremoved │ │ │ ├── petstore_v2.yaml │ │ │ └── petstore.yaml │ │ │ ├── attributeremoved-deprecated │ │ │ ├── petstore_v2.yaml │ │ │ └── petstore.yaml │ │ │ └── requestbody │ │ │ ├── anyof │ │ │ ├── propertydeleted │ │ │ │ ├── petstore_v2.yaml │ │ │ │ └── petstore.yaml │ │ │ ├── propertytypechanged │ │ │ │ ├── petstore.yaml │ │ │ │ └── petstore_v2.yaml │ │ │ └── nobreakingchange │ │ │ │ └── sameattribute │ │ │ │ └── petstore.yaml │ │ │ ├── oneof │ │ │ ├── propertydeleted │ │ │ │ ├── petstore_v2.yaml │ │ │ │ └── petstore.yaml │ │ │ └── propertytypechanged │ │ │ │ ├── petstore.yaml │ │ │ │ └── petstore_v2.yaml │ │ │ └── allof │ │ │ ├── propertydeleted │ │ │ ├── petstore_v2.yaml │ │ │ └── petstore.yaml │ │ │ ├── propertytypechanged │ │ │ ├── petstore.yaml │ │ │ └── petstore_v2.yaml │ │ │ └── nobreakingchange │ │ │ └── sameattribute │ │ │ └── petstore.yaml │ │ └── java │ │ └── com │ │ └── docktape │ │ └── swagger │ │ └── brake │ │ ├── report │ │ └── StdOutReporterTest.java │ │ ├── integration │ │ ├── v2 │ │ │ ├── response │ │ │ │ ├── EmptyResponseIntTest.java │ │ │ │ └── ResponseDeletedIntTest.java │ │ │ ├── request │ │ │ │ ├── RequestParameterRefResolutionIntTest.java │ │ │ │ └── OptionalRequestParameterAddedIntTest.java │ │ │ └── nobreakingchange │ │ │ │ ├── RefRequestBodyNoBreakingIntTest.java │ │ │ │ ├── IgnoredBreakingChangeIntTest.java │ │ │ │ └── NoBreakingChangeIntTest.java │ │ ├── v3 │ │ │ ├── schema │ │ │ │ └── EmptySchemaIntTest.java │ │ │ └── request │ │ │ │ └── requestbody │ │ │ │ └── memory │ │ │ │ └── MemoryIntTest.java │ │ └── AbstractSwaggerBrakeIntTest.java │ │ ├── maven │ │ └── maven2 │ │ │ └── ArtifactVersionDeciderTest.java │ │ └── core │ │ └── model │ │ └── transformer │ │ └── ApiResponseTransformerTest.java └── build.gradle ├── docs └── assets │ ├── img │ └── search.83621669.svg │ └── js │ ├── 4.84e1e480.js │ ├── 19.eb35cfee.js │ ├── 5.f0541060.js │ ├── 7.7551a9fb.js │ ├── 20.7d8a0e52.js │ └── 31.d14e9dcd.js ├── .travis.yml ├── swagger-brake-cli ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── docktape │ │ │ │ └── swagger │ │ │ │ └── brake │ │ │ │ └── cli │ │ │ │ ├── options │ │ │ │ ├── CliHelpException.java │ │ │ │ ├── handler │ │ │ │ │ ├── CliOptionHandler.java │ │ │ │ │ ├── NewApiPathHandler.java │ │ │ │ │ ├── OldApiPathHandler.java │ │ │ │ │ ├── GroupIdHandler.java │ │ │ │ │ ├── ApiFilenameHandler.java │ │ │ │ │ ├── ArtifactIdHandler.java │ │ │ │ │ ├── OutputPathHandler.java │ │ │ │ │ ├── MavenRepoPasswordHandler.java │ │ │ │ │ ├── MavenRepoUsernameHandler.java │ │ │ │ │ ├── BetaApiExtensionNameHandler.java │ │ │ │ │ ├── MavenRepoUrlHandler.java │ │ │ │ │ ├── CurrentArtifactVersionHandler.java │ │ │ │ │ ├── MavenSnapshotRepoUrlHandler.java │ │ │ │ │ ├── DeprecatedApiDeletionAllowedHandler.java │ │ │ │ │ ├── ArtifactPackagingHandler.java │ │ │ │ │ └── ExcludedPathsHandler.java │ │ │ │ ├── CliOptionsValidator.java │ │ │ │ └── CliOption.java │ │ │ │ ├── CliConfiguration.java │ │ │ │ └── SwaggerBrakeMain.java │ │ └── resources │ │ │ └── logback.xml │ └── test │ │ ├── resources │ │ └── logback.xml │ │ └── java │ │ └── com │ │ └── docktape │ │ └── swagger │ │ └── brake │ │ └── cli │ │ └── options │ │ └── CliOptionsProviderTest.java └── build.gradle ├── .github └── workflows │ ├── docker-build.yml │ └── integration-tests.yml ├── Dockerfile └── renovate.json /config/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-example/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docktape/swagger-brake/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-maven-example/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src-docs/src/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | export default ({ 2 | Vue, 3 | options, 4 | router, 5 | siteData 6 | }) => { 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.vfs.watch=true -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = '' 2 | 3 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'swagger-brake-root' 2 | 3 | include 'swagger-brake' 4 | include 'swagger-brake-cli' 5 | include 'swagger-brake-integration-tests' 6 | 7 | -------------------------------------------------------------------------------- /src-docs/.npmignore: -------------------------------------------------------------------------------- 1 | pids 2 | logs 3 | node_modules 4 | npm-debug.log 5 | coverage/ 6 | run 7 | dist 8 | .DS_Store 9 | .nyc_output 10 | .basement 11 | config.local.js 12 | basement_dist 13 | -------------------------------------------------------------------------------- /src-docs/src/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Styles here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl 5 | */ 6 | 7 | .home .hero img 8 | max-width 450px!important 9 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/OutputFormat.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner; 2 | 3 | public enum OutputFormat { 4 | STDOUT, 5 | JSON, 6 | HTML 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/service/Resolver.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.service; 2 | 3 | public interface Resolver { 4 | R resolve(S from); 5 | } 6 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/BreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | public interface BreakingChange { 4 | String getMessage(); 5 | 6 | String getRuleCode(); 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/transformer/Transformer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.transformer; 2 | 3 | public interface Transformer { 4 | R transform(S from); 5 | } 6 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-example/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/ConstrainedValue.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | public interface ConstrainedValue { 4 | } 5 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-maven-example/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip -------------------------------------------------------------------------------- /docs/assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-example/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:21-alpine-jdk 2 | 3 | RUN env 4 | WORKDIR swagger-brake-example 5 | COPY . . 6 | ENTRYPOINT ["sh", "mvnw", "clean", "deploy"] 7 | 8 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:21-alpine-jdk 2 | 3 | WORKDIR swagger-brake-gradle-example 4 | COPY . . 5 | ENTRYPOINT chmod +x entrypoint.sh && sh entrypoint.sh -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-maven-example/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:21-alpine-jdk 2 | 3 | WORKDIR swagger-brake-maven-example 4 | COPY . . 5 | ENTRYPOINT chmod +x entrypoint.sh && sh entrypoint.sh -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/NoConstrainedValue.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | public class NoConstrainedValue implements ConstrainedValue { 4 | } 5 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/LatestArtifactDownloader.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven; 2 | 3 | import java.io.File; 4 | 5 | public interface LatestArtifactDownloader { 6 | File download(DownloadOptions options); 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | public enum HttpMethod { 4 | GET, 5 | HEAD, 6 | POST, 7 | PUT, 8 | DELETE, 9 | PATCH, 10 | OPTIONS, 11 | TRACE 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk9 4 | os: 5 | - linux 6 | before_cache: 7 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 8 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 9 | cache: 10 | directories: 11 | - $HOME/.gradle/caches/ 12 | - $HOME/.gradle/wrapper/ -------------------------------------------------------------------------------- /src-docs/src/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom palette here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl 5 | */ 6 | 7 | $accentColor = #3eaf7c 8 | $textColor = #2c3e50 9 | $borderColor = #eaecef 10 | $codeBgColor = #282c34 11 | 12 | $sidebarWidth = 25rem -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/CliHelpException.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options; 2 | 3 | public class CliHelpException extends RuntimeException { 4 | public CliHelpException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/ApiInfo.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ApiInfo { 7 | private String title; 8 | private String description; 9 | private String version; 10 | } 11 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/http/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.http; 2 | 3 | public class UnauthorizedException extends RuntimeException { 4 | public UnauthorizedException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/CheckableReporter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report; 2 | 3 | import com.docktape.swagger.brake.runner.OutputFormat; 4 | 5 | public interface CheckableReporter extends Reporter { 6 | boolean canReport(OutputFormat format); 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docktape/swagger-brake/HEAD/swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/filename/ApiFileNameChecker.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar.filename; 2 | 3 | import org.springframework.core.Ordered; 4 | 5 | public interface ApiFileNameChecker extends Ordered { 6 | boolean isApiFile(String fileName); 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/ArtifactVersionDecider.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | public abstract class ArtifactVersionDecider { 4 | public static boolean isSnapshot(String version) { 5 | return version.endsWith("-SNAPSHOT"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/BuildFailureException.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project; 2 | 3 | public class BuildFailureException extends RuntimeException { 4 | public BuildFailureException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/CliConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ComponentScan 8 | public class CliConfiguration { 9 | } 10 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-gradle-example/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/CoreConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ComponentScan 8 | public class CoreConfiguration { 9 | } 10 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/RunnerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ComponentScan 8 | public class RunnerConfiguration { 9 | } 10 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/BreakChecker.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | import java.util.Collection; 4 | 5 | import com.docktape.swagger.brake.core.model.Specification; 6 | 7 | public interface BreakChecker { 8 | Collection check(Specification oldApi, Specification newApi); 9 | } 10 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/exception/LatestArtifactDownloadException.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner.exception; 2 | 3 | public class LatestArtifactDownloadException extends RuntimeException { 4 | public LatestArtifactDownloadException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/html/HtmlData.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report.html; 2 | 3 | import java.util.Collection; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class HtmlData { 9 | private Collection breakingChanges; 10 | private Collection ignoredBreakingChanges; 11 | } 12 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/support/ArtifactIdGenerator.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.support; 2 | 3 | import org.apache.commons.lang3.RandomStringUtils; 4 | 5 | public class ArtifactIdGenerator { 6 | public static String generate() { 7 | return RandomStringUtils.randomAlphanumeric(12); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/PackagingType.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @RequiredArgsConstructor 7 | @Getter 8 | public enum PackagingType { 9 | JAR("jar"), WAR("war"); 10 | 11 | private final String packaging; 12 | } 13 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/support/SwaggerExamples.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.support; 2 | 3 | public class SwaggerExamples { 4 | public static final String EXAMPLE_SWAGGER_1 = "example-swaggers/artifactory/swagger1.yaml"; 5 | public static final String EXAMPLE_SWAGGER_2 = "example-swaggers/artifactory/swagger2.yaml"; 6 | } 7 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/Constraint.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | import java.util.Optional; 4 | 5 | public interface Constraint { 6 | Optional validateConstraints(T oldRequestParameter, T newRequestParameter); 7 | 8 | Class handledRequestParameter(); 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Docker build 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-24.04 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 18 | - name: Build image 19 | run: docker build . -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build 2 | FROM amazoncorretto:21-alpine-jdk AS baseimage 3 | 4 | WORKDIR swagger-brake 5 | COPY . . 6 | RUN ./gradlew --no-daemon clean build shadowJar -x test 7 | 8 | # Actual container 9 | FROM amazoncorretto:21-alpine-jdk 10 | WORKDIR swagger-brake 11 | COPY --from=baseimage /swagger-brake/swagger-brake-cli/build/libs/swagger-brake-*-cli.jar swagger-brake.jar 12 | 13 | ENTRYPOINT ["java", "-jar", "swagger-brake.jar"] 14 | 15 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/BreakingChangeRule.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule; 2 | 3 | import java.util.Collection; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.Specification; 7 | 8 | public interface BreakingChangeRule { 9 | Collection checkRule(Specification oldApi, Specification newApi); 10 | } 11 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/CheckerOptions.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | import java.util.Collections; 4 | import java.util.Set; 5 | 6 | import lombok.Data; 7 | 8 | @Data 9 | public class CheckerOptions { 10 | private boolean deprecatedApiDeletionAllowed = true; 11 | private String betaApiExtensionName = "x-beta-api"; 12 | private Set excludedPaths = Collections.emptySet(); 13 | } 14 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/CliOptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | 6 | public interface CliOptionHandler { 7 | void handle(String optionValue, Options options); 8 | 9 | CliOption getHandledCliOption(); 10 | 11 | String getHelpMessage(); 12 | } 13 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/docker/NetworkHolder.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.docker; 2 | 3 | import org.testcontainers.containers.Network; 4 | 5 | public class NetworkHolder { 6 | private static final Network INSTANCE = Network.newNetwork(); 7 | 8 | private NetworkHolder() { 9 | } 10 | 11 | public static Network getInstance() { 12 | return INSTANCE; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/file/DirectoryCreator.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report.file; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Paths; 6 | 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class DirectoryCreator { 11 | public void create(String path) throws IOException { 12 | Files.createDirectories(Paths.get(path)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/ApiFileResolverParameter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar; 2 | 3 | import java.io.File; 4 | 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | @Getter 10 | @RequiredArgsConstructor 11 | @EqualsAndHashCode 12 | public class ApiFileResolverParameter { 13 | private final File apiJar; 14 | private final String configuredApiFilename; 15 | } 16 | -------------------------------------------------------------------------------- /docs/assets/js/4.84e1e480.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{277:function(t,e,n){},291:function(t,e,n){"use strict";n(277)},302:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:(t,{props:e,slots:n})=>t("span",{class:["badge",e.type],style:{verticalAlign:e.vertical}},e.text||n().default)},p=(n(291),n(14)),l=Object(p.a)(i,void 0,void 0,!1,null,"15b7b770",null);e.default=l.exports}}]); -------------------------------------------------------------------------------- /src-docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-brake", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "authors": { 7 | "name": "Arnold Galovics", 8 | "email": "" 9 | }, 10 | "repository": "/swagger-brake", 11 | "scripts": { 12 | "dev": "vuepress dev src", 13 | "build": "vuepress build src" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@vuepress/plugin-google-analytics": "1.9.10", 18 | "vuepress": "1.9.10" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/cli/CliProjectParameter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project.cli; 2 | 3 | import com.docktape.swagger.brake.integration.project.ProjectParameter; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | @Data 8 | @EqualsAndHashCode(callSuper = true) 9 | public class CliProjectParameter extends ProjectParameter { 10 | private String groupId; 11 | private String artifactId; 12 | } 13 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/http/Base64Utils.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.http; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.Base64; 5 | 6 | abstract class Base64Utils { 7 | static String encode(String s) { 8 | byte[] pureBytes = s.getBytes(StandardCharsets.UTF_8); 9 | byte[] encodedBytes = Base64.getEncoder().encode(pureBytes); 10 | return new String(encodedBytes, StandardCharsets.UTF_8); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/support/SwaggerBrakeVersion.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.support; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class SwaggerBrakeVersion { 7 | public static final SwaggerBrakeVersion V_LATEST_VERSION = new SwaggerBrakeVersion("2.6.0"); 8 | 9 | private final String version; 10 | 11 | private SwaggerBrakeVersion(String version) { 12 | this.version = version; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src-docs/src/troubleshooting/README.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | ## CLI file reporting doesn't work 3 | If you encounter the following message during an execution 4 | ```text 5 | No file reporting has been done since output file path is not set 6 | ``` 7 | It means you have configured file typed reporting like JSON or HTML but you did not pass the 8 | `--output-path` argument so swagger-brake doesn't know where to store the reports. 9 | 10 | Relevant section: [Customizing reporting](../cli/cli-interface.md#customizing-reporting) 11 | 12 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/resources/example-projects/swagger-brake-maven-example/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -d "local-swagger-brake" ]; then 4 | mkdir -p /root/.m2/repository/com/docktape/ 5 | cp -r local-swagger-brake/* /root/.m2/repository/com/docktape/ 6 | echo "Local Swagger Brake has been successfully copied from the host" 7 | fi 8 | 9 | if [ "$REMOVE_PACKAGING" == "true" ] 10 | then 11 | sed -i "s/\${custom.packaging}<\/packaging>/ /g" pom.xml 12 | fi 13 | 14 | sh mvnw clean deploy -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/model/MavenSnapshot.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.model; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @JacksonXmlRootElement(localName = "snapshot") 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class MavenSnapshot { 13 | private String timestamp; 14 | private String buildNumber; 15 | } 16 | -------------------------------------------------------------------------------- /docs/assets/js/19.eb35cfee.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[19],{240:function(t,e,n){},242:function(t,e,n){"use strict";n.r(e);var s={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},i=(n(243),n(14)),o=Object(i.a)(s,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=o.exports},243:function(t,e,n){"use strict";n(240)}}]); -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/service/RequestParameterInTypeResolver.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.service; 2 | 3 | import com.docktape.swagger.brake.core.model.RequestParameterInType; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | public class RequestParameterInTypeResolver implements Resolver { 8 | @Override 9 | public RequestParameterInType resolve(String from) { 10 | return RequestParameterInType.fromName(from); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/assets/js/5.f0541060.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{278:function(t,e,a){},292:function(t,e,a){"use strict";a(278)},298:function(t,e,a){"use strict";a.r(e);var s={name:"CodeBlock",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}},mounted(){this.$parent&&this.$parent.loadTabs&&this.$parent.loadTabs()}},i=(a(292),a(14)),n=Object(i.a)(s,(function(){return(0,this._self._c)("div",{staticClass:"theme-code-block",class:{"theme-code-block__active":this.active}},[this._t("default")],2)}),[],!1,null,"759a7d02",null);e.default=n.exports}}]); -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/ConstraintChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.ToString; 7 | 8 | @RequiredArgsConstructor 9 | @Getter 10 | @ToString 11 | @EqualsAndHashCode 12 | public class ConstraintChange { 13 | private final String attribute; 14 | private final Object oldValue; 15 | private final Object newValue; 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/ParameterStore.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | import java.util.Map; 4 | import java.util.Optional; 5 | 6 | import io.swagger.v3.oas.models.parameters.Parameter; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | @RequiredArgsConstructor 10 | public class ParameterStore { 11 | private final Map parameters; 12 | 13 | public Optional get(String name) { 14 | return Optional.ofNullable(parameters.get(name)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/ResponseStore.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | import java.util.Map; 4 | import java.util.Optional; 5 | 6 | import io.swagger.v3.oas.models.responses.ApiResponse; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | @RequiredArgsConstructor 10 | public class ResponseStore { 11 | private final Map responses; 12 | 13 | public Optional get(String name) { 14 | return Optional.ofNullable(responses.get(name)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/StringConstrainedValue.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.ToString; 7 | 8 | @Getter 9 | @EqualsAndHashCode 10 | @ToString 11 | @RequiredArgsConstructor 12 | public class StringConstrainedValue implements ConstrainedValue { 13 | private final Integer maxLength; 14 | private final Integer minLength; 15 | } 16 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/ResponsesTransformer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | import com.docktape.swagger.brake.core.model.transformer.Transformer; 4 | import io.swagger.v3.oas.models.Components; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class ResponsesTransformer implements Transformer { 9 | @Override 10 | public ResponseStore transform(Components from) { 11 | return new ResponseStore(from.getResponses()); 12 | } 13 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/model/MavenMetadata.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.model; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @JacksonXmlRootElement(localName = "metadata") 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class MavenMetadata { 13 | private String groupId; 14 | private String artifactId; 15 | private MavenVersioning versioning; 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/model/MavenVersioning.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.model; 2 | 3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @JacksonXmlRootElement(localName = "versioning") 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class MavenVersioning { 13 | private String latest; 14 | private String release; 15 | private MavenSnapshot snapshot; 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/ParametersTransformer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | import com.docktape.swagger.brake.core.model.transformer.Transformer; 4 | import io.swagger.v3.oas.models.Components; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class ParametersTransformer implements Transformer { 9 | @Override 10 | public ParameterStore transform(Components from) { 11 | return new ParameterStore(from.getParameters()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/util/BigDecimalComparator.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.util; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class BigDecimalComparator { 6 | public static boolean isLessThan(BigDecimal a, BigDecimal b) { 7 | return a.compareTo(b) < 0; 8 | } 9 | 10 | public static boolean isEqual(BigDecimal a, BigDecimal b) { 11 | return a.compareTo(b) == 0; 12 | } 13 | 14 | public static boolean isGreaterThan(BigDecimal a, BigDecimal b) { 15 | return a.compareTo(b) > 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/path/deleted/deprecated/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | version: "1.0.0" 5 | title: "Swagger Petstore" 6 | basePath: "/v2" 7 | paths: 8 | /pet/findByTags: 9 | get: 10 | summary: "Finds Pets by tags" 11 | operationId: "findPetsByTags" 12 | produces: 13 | - "application/json" 14 | responses: 15 | 200: 16 | description: "successful operation" 17 | definitions: 18 | Pet: 19 | type: "object" 20 | properties: 21 | id: 22 | type: "integer" 23 | format: "int64" -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/ArrayConstrainedValue.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.ToString; 7 | 8 | @Getter 9 | @EqualsAndHashCode 10 | @ToString 11 | @RequiredArgsConstructor 12 | public class ArrayConstrainedValue implements ConstrainedValue { 13 | private final Integer maxItems; 14 | private final Integer minItems; 15 | private final Boolean uniqueItems; 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/filename/SwaggerApiFileNameChecker.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar.filename; 2 | 3 | import org.springframework.core.Ordered; 4 | 5 | class SwaggerApiFileNameChecker implements ApiFileNameChecker { 6 | @Override 7 | public boolean isApiFile(String fileName) { 8 | return fileName.endsWith("swagger.json") 9 | || fileName.endsWith("swagger.yml") 10 | || fileName.endsWith("swagger.yaml"); 11 | } 12 | 13 | @Override 14 | public int getOrder() { 15 | return Ordered.LOWEST_PRECEDENCE; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/service/TypeRefNameResolver.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.service; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class TypeRefNameResolver implements Resolver { 7 | @Override 8 | public String resolve(String from) { 9 | return getNameFromRef(from); 10 | } 11 | 12 | private String getNameFromRef(String refName) { 13 | if (refName == null) { 14 | return null; 15 | } 16 | return refName.substring(refName.lastIndexOf("/") + 1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/ProjectParameter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ProjectParameter { 7 | private String customVersion; 8 | private String artifactoryContextUrl; 9 | private String releaseRepoKey; 10 | private String releaseRepoUrl; 11 | private String snapshotRepoKey; 12 | private String snapshotRepoUrl; 13 | private String username; 14 | private String password; 15 | private String classpathSwagger; 16 | private PackagingType packagingType; 17 | } 18 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/artifactory/page/ArtifactoryRepositoriesPage.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.artifactory.page; 2 | 3 | import org.openqa.selenium.WebElement; 4 | import org.openqa.selenium.support.FindBy; 5 | 6 | public class ArtifactoryRepositoriesPage extends PageBase { 7 | @FindBy(className = "new-entity") 8 | private WebElement newLocalRepositoryButton; 9 | 10 | public ArtifactoryNewRepositoryPage newLocalRepository() { 11 | click(newLocalRepositoryButton); 12 | return proceed(ArtifactoryNewRepositoryPage.class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/ComponentsTransformer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | import com.docktape.swagger.brake.core.model.transformer.Transformer; 4 | import io.swagger.v3.oas.models.Components; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @RequiredArgsConstructor 10 | public class ComponentsTransformer implements Transformer { 11 | @Override 12 | public SchemaStore transform(Components from) { 13 | return new SchemaStore(from.getSchemas()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/util/DecimalFormatter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.util; 2 | 3 | import java.math.BigDecimal; 4 | import java.text.DecimalFormat; 5 | 6 | public class DecimalFormatter { 7 | private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat(); 8 | 9 | static { 10 | DECIMAL_FORMAT.setMaximumFractionDigits(2); 11 | DECIMAL_FORMAT.setMinimumFractionDigits(0); 12 | DECIMAL_FORMAT.setGroupingUsed(false); 13 | } 14 | 15 | public static String format(BigDecimal bigDecimal) { 16 | return DECIMAL_FORMAT.format(bigDecimal); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/support/ClasspathFileLoader.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.support; 2 | 3 | import java.io.File; 4 | import java.net.URISyntaxException; 5 | 6 | public class ClasspathFileLoader { 7 | public static String getAbsolutePath(String classpathResourcePath) { 8 | try { 9 | return new File(ClasspathFileLoader.class.getClassLoader().getResource(classpathResourcePath).toURI()).getAbsolutePath(); 10 | } catch (URISyntaxException e) { 11 | throw new RuntimeException("Unable to get absolute path", e); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/assets/js/7.7551a9fb.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{300:function(t,e,s){"use strict";s.r(e);const o=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."];var n={methods:{getMsg:()=>o[Math.floor(Math.random()*o.length)]}},h=s(14),i=Object(h.a)(n,(function(){var t=this._self._c;return t("div",{staticClass:"theme-container"},[t("div",{staticClass:"theme-default-content"},[t("h1",[this._v("404")]),this._v(" "),t("blockquote",[this._v(this._s(this.getMsg()))]),this._v(" "),t("RouterLink",{attrs:{to:"/"}},[this._v("\n Take me home.\n ")])],1)])}),[],!1,null,null,null);e.default=i.exports}}]); -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/plugin/PluginProjectParameter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project.plugin; 2 | 3 | import com.docktape.swagger.brake.integration.project.ProjectParameter; 4 | import com.docktape.swagger.brake.integration.support.SwaggerBrakeVersion; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | @EqualsAndHashCode(callSuper = true) 10 | public class PluginProjectParameter extends ProjectParameter { 11 | private SwaggerBrakeVersion swaggerBrakeVersion; 12 | private boolean removePackaging = false; 13 | private String artifactId; 14 | } 15 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/number/PrettyFormattedBigDecimal.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint.number; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import com.docktape.swagger.brake.core.util.DecimalFormatter; 6 | 7 | public class PrettyFormattedBigDecimal extends BigDecimal { 8 | public PrettyFormattedBigDecimal(BigDecimal value) { 9 | super(value.unscaledValue(), value.scale()); 10 | } 11 | 12 | @Override 13 | public String toString() { 14 | return DecimalFormatter.format(new BigDecimal(this.unscaledValue(), this.scale())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/NumberConstrainedValue.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.ToString; 9 | 10 | @Getter 11 | @EqualsAndHashCode 12 | @ToString 13 | @RequiredArgsConstructor 14 | public class NumberConstrainedValue implements ConstrainedValue { 15 | private final BigDecimal maximum; 16 | private final BigDecimal minimum; 17 | private final boolean exclusiveMaximum; 18 | private final boolean exclusiveMinimum; 19 | } 20 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/transformer/MediaTypeTransformer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.transformer; 2 | 3 | import com.docktape.swagger.brake.core.model.Schema; 4 | import io.swagger.v3.oas.models.media.MediaType; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @RequiredArgsConstructor 10 | public class MediaTypeTransformer implements Transformer { 11 | private final SchemaTransformer schemaTransformer; 12 | 13 | @Override 14 | public Schema transform(MediaType from) { 15 | return schemaTransformer.transform(from.getSchema()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/MediaType.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.ToString; 6 | import org.springframework.util.MimeType; 7 | import org.springframework.util.MimeTypeUtils; 8 | 9 | @Getter 10 | @EqualsAndHashCode 11 | @ToString 12 | public class MediaType { 13 | public static final MediaType ALL = new MediaType("*/*"); 14 | 15 | private final MimeType mediaType; 16 | private final String mimeType; 17 | 18 | public MediaType(String mimeType) { 19 | this.mimeType = mimeType; 20 | this.mediaType = MimeTypeUtils.parseMimeType(mimeType); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/ArtifactPackaging.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner; 2 | 3 | import java.util.Arrays; 4 | 5 | import lombok.Getter; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | 9 | @RequiredArgsConstructor 10 | @Getter 11 | public enum ArtifactPackaging { 12 | JAR("jar"), WAR("war"); 13 | 14 | private final String packaging; 15 | 16 | public static ArtifactPackaging forPackaging(String packaging) { 17 | return Arrays.stream(ArtifactPackaging.values()).filter(p -> p.getPackaging().equalsIgnoreCase(packaging)) 18 | .findAny().orElseThrow(() -> new IllegalArgumentException("Packaging " + packaging + " is not supported")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | %msg%n 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/DownloadOptions.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven; 2 | 3 | import com.docktape.swagger.brake.runner.ArtifactPackaging; 4 | import lombok.Data; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | @Data 8 | public class DownloadOptions { 9 | private String repoUrl; 10 | private String snapshotRepoUrl; 11 | private String groupId; 12 | private String artifactId; 13 | private String username; 14 | private String password; 15 | private String currentArtifactVersion; 16 | private ArtifactPackaging artifactPackaging; 17 | 18 | public boolean isAuthenticationNeeded() { 19 | return StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/filename/ApiFileNameCheckerChain.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar.filename; 2 | 3 | import java.util.Collection; 4 | 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @RequiredArgsConstructor 8 | class ApiFileNameCheckerChain implements ApiFileNameChecker { 9 | private final Collection checkers; 10 | 11 | @Override 12 | public boolean isApiFile(String fileName) { 13 | for (ApiFileNameChecker checker : checkers) { 14 | if (checker.isApiFile(fileName)) { 15 | return true; 16 | } 17 | } 18 | return false; 19 | } 20 | 21 | @Override 22 | public int getOrder() { 23 | return 0; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/openapi/ApiInfoFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner.openapi; 2 | 3 | import com.docktape.swagger.brake.core.ApiInfo; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class ApiInfoFactory { 10 | public ApiInfo create(OpenAPI openApi) { 11 | ApiInfo apiInfo = new ApiInfo(); 12 | Info info = openApi.getInfo(); 13 | if (info != null) { 14 | apiInfo.setTitle(info.getTitle()); 15 | apiInfo.setDescription(info.getDescription()); 16 | apiInfo.setVersion(info.getVersion()); 17 | } 18 | return apiInfo; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/Specification.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Collection; 4 | import java.util.Optional; 5 | 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.ToString; 10 | 11 | @Getter 12 | @RequiredArgsConstructor 13 | @EqualsAndHashCode 14 | @ToString 15 | public class Specification { 16 | private final Collection paths; 17 | 18 | public Optional getPath(Path path) { 19 | return getPath(path.getPath(), path.getMethod()); 20 | } 21 | 22 | public Optional getPath(String path, HttpMethod method) { 23 | return paths.stream().filter(p -> path.equals(p.getPath())).filter(p -> method.equals(p.getMethod())).findAny(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/ReporterConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.SerializationFeature; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Primary; 9 | 10 | @Configuration 11 | @ComponentScan 12 | public class ReporterConfiguration { 13 | @Bean 14 | @Primary 15 | public ObjectMapper objectMapper() { 16 | ObjectMapper objectMapper = new ObjectMapper(); 17 | objectMapper.enable(SerializationFeature.INDENT_OUTPUT); 18 | return objectMapper; 19 | } 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/html/BreakingChangeTableRow.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report.html; 2 | 3 | import com.fasterxml.jackson.annotation.JsonUnwrapped; 4 | import com.docktape.swagger.brake.core.BreakingChange; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class BreakingChangeTableRow { 9 | @JsonUnwrapped 10 | private BreakingChange breakingChange; 11 | private String message; 12 | private String ruleCode; 13 | 14 | /** 15 | * Constructs a table row for reporting purposes. 16 | * @param breakingChange the breaking change 17 | */ 18 | public BreakingChangeTableRow(BreakingChange breakingChange) { 19 | this.breakingChange = breakingChange; 20 | this.message = breakingChange.getMessage(); 21 | this.ruleCode = breakingChange.getRuleCode(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /swagger-brake/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | } 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | implementation "org.apache.commons:commons-collections4:4.5.0" 13 | implementation "io.swagger.parser.v3:swagger-parser:2.1.35" 14 | implementation "com.fasterxml.jackson.core:jackson-databind:2.19.2" 15 | implementation "com.github.spullara.mustache.java:compiler:0.9.14" 16 | implementation "org.apache.httpcomponents:httpclient:4.5.14" 17 | 18 | testImplementation 'org.junit.jupiter:junit-jupiter:6.0.1' 19 | testRuntimeOnly 'org.junit.platform:junit-platform-launcher' 20 | testImplementation 'io.github.classgraph:classgraph:4.8.184' 21 | } 22 | 23 | tasks.named('test', Test) { 24 | useJUnitPlatform() 25 | 26 | testLogging { 27 | events "passed" 28 | } 29 | } -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/path/deleted/deprecated/petstore.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | version: "1.0.0" 5 | title: "Swagger Petstore" 6 | basePath: "/v2" 7 | paths: 8 | /pet/findByStatus: 9 | get: 10 | deprecated: true 11 | summary: "Finds Pets by status" 12 | operationId: "findPetsByStatus" 13 | produces: 14 | - "application/json" 15 | responses: 16 | 200: 17 | description: "successful operation" 18 | /pet/findByTags: 19 | get: 20 | summary: "Finds Pets by tags" 21 | operationId: "findPetsByTags" 22 | produces: 23 | - "application/json" 24 | responses: 25 | 200: 26 | description: "successful operation" 27 | definitions: 28 | Pet: 29 | type: "object" 30 | properties: 31 | id: 32 | type: "integer" 33 | format: "int64" -------------------------------------------------------------------------------- /docs/assets/js/20.7d8a0e52.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[20],{257:function(t,c,n){},270:function(t,c,n){"use strict";n(257)},283:function(t,c,n){"use strict";n.r(c);n(270);var i=n(14),s=Object(i.a)({},(function(){var t=this,c=t._self._c;return c("div",{staticClass:"sidebar-button",on:{click:function(c){return t.$emit("toggle-sidebar")}}},[c("svg",{staticClass:"icon",attrs:{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",role:"img",viewBox:"0 0 448 512"}},[c("path",{attrs:{fill:"currentColor",d:"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"}})])])}),[],!1,null,null,null);c.default=s.exports}}]); -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/Reporter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report; 2 | 3 | import com.docktape.swagger.brake.core.ApiInfo; 4 | import com.docktape.swagger.brake.core.BreakingChange; 5 | import com.docktape.swagger.brake.runner.Options; 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | 9 | public interface Reporter { 10 | default void report(Collection breakingChanges, Options options) { 11 | report(breakingChanges, options, null); 12 | } 13 | 14 | default void report(Collection breakingChanges, Options options, ApiInfo apiInfo) { 15 | report(breakingChanges, Collections.emptyList(), options, apiInfo); 16 | } 17 | 18 | void report(Collection breakingChanges, Collection ignoredBreakingChanges, Options options, ApiInfo apiInfo); 19 | } 20 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/response/attributeremoved/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | tags: 21 | - pets 22 | responses: 23 | '201': 24 | description: Null response 25 | content: 26 | application/json: 27 | schema: 28 | type: object 29 | components: 30 | schemas: 31 | Pet: 32 | type: object 33 | properties: 34 | name: 35 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/util/PathNormalizer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | public abstract class PathNormalizer { 6 | /** 7 | * Normalizes a path, adding leading and trailing slashes if needed. 8 | * @param path the path to be normalized 9 | * @return the normalized path 10 | */ 11 | public static String normalizePathSlashes(String path) { 12 | if (StringUtils.isBlank(path)) { 13 | throw new IllegalArgumentException("path cannot be blank"); 14 | } 15 | String result = path; 16 | if (!result.startsWith("/")) { 17 | result = "/" + result; 18 | } 19 | if (result.endsWith("/")) { 20 | result = result.substring(0, result.length() - 1); 21 | } 22 | return result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/response/attributeremoved-deprecated/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | tags: 21 | - pets 22 | responses: 23 | '201': 24 | description: Null response 25 | content: 26 | application/json: 27 | schema: 28 | type: object 29 | components: 30 | schemas: 31 | Pet: 32 | type: object 33 | properties: 34 | name: 35 | type: string -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/NewApiPathHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | class NewApiPathHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setNewApiPath(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.NEW_API_PATH; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "The absolute path of the new api file"; 25 | } 26 | } -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/OldApiPathHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | class OldApiPathHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setOldApiPath(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.OLD_API_PATH; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "The absolute path of the old api file"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/path/PathDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.path; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class PathDeletedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | 20 | @Override 21 | public String getMessage() { 22 | return format("Path %s %s has been deleted", path, method); 23 | } 24 | 25 | @Override 26 | public String getRuleCode() { 27 | return "R002"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/SchemaAttribute.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.ToString; 7 | 8 | @Getter 9 | @RequiredArgsConstructor 10 | @EqualsAndHashCode(of = "name") 11 | @ToString 12 | public class SchemaAttribute implements Comparable { 13 | private final String name; 14 | private final Schema schema; 15 | private final boolean required; 16 | private final boolean deprecated; 17 | 18 | public String getName() { 19 | return name.replace(Schema.LEVEL_DELIMITER_REPLACE_VALUE, Schema.LEVEL_DELIMITER); 20 | } 21 | 22 | public String getType() { 23 | return schema.getType(); 24 | } 25 | 26 | @Override 27 | public int compareTo(SchemaAttribute o) { 28 | return name.compareTo(o.name); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/report/StdOutReporterTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.docktape.swagger.brake.runner.OutputFormat; 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class StdOutReporterTest { 9 | private StdOutReporter underTest = new StdOutReporter(); 10 | 11 | @Test 12 | public void testCanReportShouldReturnTrueIfOutputFormatIsStdOut() { 13 | // given 14 | // when 15 | boolean result = underTest.canReport(OutputFormat.STDOUT); 16 | // then 17 | assertThat(result).isTrue(); 18 | } 19 | 20 | @Test 21 | public void testCanReportShouldReturnFalseIfOutputFormatIsHtml() { 22 | // given 23 | // when 24 | boolean result = underTest.canReport(OutputFormat.HTML); 25 | // then 26 | assertThat(result).isTrue(); 27 | } 28 | } -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/GroupIdHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class GroupIdHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setGroupId(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.GROUP_ID; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the group id which will be used for latest version resolution"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/ApiFilenameHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class ApiFilenameHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setApiFilename(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.API_FILENAME; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the API filename Swagger Brake is going to search for"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/ArtifactIdHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class ArtifactIdHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setArtifactId(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.ARTIFACT_ID; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the artifact id which will be used for latest version resolution"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/transformer/PathDetail.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.transformer; 2 | 3 | import java.util.Collection; 4 | 5 | import com.docktape.swagger.brake.core.model.HttpMethod; 6 | import com.docktape.swagger.brake.core.model.Request; 7 | import com.docktape.swagger.brake.core.model.Response; 8 | import com.docktape.swagger.brake.core.model.parameter.RequestParameter; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.Getter; 11 | import lombok.RequiredArgsConstructor; 12 | import lombok.ToString; 13 | 14 | @Getter 15 | @RequiredArgsConstructor 16 | @EqualsAndHashCode 17 | @ToString 18 | class PathDetail { 19 | private final HttpMethod method; 20 | private final Request requestBody; 21 | private final Collection requestParameters; 22 | private final Collection responses; 23 | private final boolean deprecated; 24 | private final boolean betaApi; 25 | } 26 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/beta/StandardApiToBetaApiBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.beta; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class StandardApiToBetaApiBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | 20 | @Override 21 | public String getMessage() { 22 | return format("Path %s %s has been as beta however it was already present.", path, method); 23 | } 24 | 25 | @Override 26 | public String getRuleCode() { 27 | return "R001"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/OutputPathHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class OutputPathHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setOutputFilePath(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.OUTPUT_PATH; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the folder for the output. Only applicable in case of file output formats."; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/MavenRepoPasswordHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import static org.apache.commons.lang3.StringUtils.isNotBlank; 4 | 5 | import com.docktape.swagger.brake.cli.options.CliOption; 6 | import com.docktape.swagger.brake.runner.Options; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class MavenRepoPasswordHandler implements CliOptionHandler { 11 | @Override 12 | public void handle(String optionValue, Options options) { 13 | if (isNotBlank(optionValue)) { 14 | options.setMavenRepoPassword(optionValue); 15 | } 16 | } 17 | 18 | @Override 19 | public CliOption getHandledCliOption() { 20 | return CliOption.MAVEN_REPO_PASSWORD; 21 | } 22 | 23 | @Override 24 | public String getHelpMessage() { 25 | return "Specifies the password to access the Maven repository"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/MavenRepoUsernameHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import static org.apache.commons.lang3.StringUtils.isNotBlank; 4 | 5 | import com.docktape.swagger.brake.cli.options.CliOption; 6 | import com.docktape.swagger.brake.runner.Options; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class MavenRepoUsernameHandler implements CliOptionHandler { 11 | @Override 12 | public void handle(String optionValue, Options options) { 13 | if (isNotBlank(optionValue)) { 14 | options.setMavenRepoUsername(optionValue); 15 | } 16 | } 17 | 18 | @Override 19 | public CliOption getHandledCliOption() { 20 | return CliOption.MAVEN_REPO_USERNAME; 21 | } 22 | 23 | @Override 24 | public String getHelpMessage() { 25 | return "Specifies the username to access the Maven repository"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/LatestArtifactDownloaderFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven; 2 | 3 | import com.docktape.swagger.brake.runner.Options; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @RequiredArgsConstructor 10 | public class LatestArtifactDownloaderFactory { 11 | private final ApplicationContext applicationContext; 12 | 13 | /** 14 | * Factory that constructs a {@link LatestArtifactDownloader} instance. 15 | * @param options the {@link Options} with which the {@link LatestArtifactDownloader} can be configured. 16 | * @return the created {@link LatestArtifactDownloader} instance. 17 | */ 18 | public LatestArtifactDownloader create(Options options) { 19 | return applicationContext.getBean("maven2LatestArtifactDownloader", LatestArtifactDownloader.class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/response/ResponseDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.response; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class ResponseDeletedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String code; 20 | 21 | @Override 22 | public String getMessage() { 23 | return format("Response %s has been removed from %s %s", code, method, path); 24 | } 25 | 26 | @Override 27 | public String getRuleCode() { 28 | return "R012"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/artifactory/ArtifactoryServerPool.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.artifactory; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.testcontainers.containers.Network; 5 | 6 | @Slf4j 7 | public class ArtifactoryServerPool { 8 | private static final ArtifactoryServerPool INSTANCE = new ArtifactoryServerPool(); 9 | 10 | private final Object mutex = new Object(); 11 | private ArtifactoryServer server = null; 12 | 13 | private ArtifactoryServerPool() { 14 | } 15 | 16 | public ArtifactoryServer provide(Network network) { 17 | synchronized (mutex) { 18 | if (server == null) { 19 | server = new ArtifactoryServer(network); 20 | server.initialize(); 21 | } 22 | return server; 23 | } 24 | } 25 | 26 | public static ArtifactoryServerPool getInstance() { 27 | return INSTANCE; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/attributeremoved/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | breed: 32 | type: string 33 | enum: [Dingo, Husky, Retriever, Shepherd] 34 | Pet: 35 | type: object 36 | properties: 37 | name: 38 | type: string -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/BetaApiExtensionNameHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class BetaApiExtensionNameHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setBetaApiExtensionName(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.BETA_API_EXTENSION_NAME; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the beta API extension attribute name. Defaults to 'x-beta-api'."; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestParameterDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestParameterDeletedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String name; 20 | 21 | @Override 22 | public String getMessage() { 23 | return format("%s parameter has been deleted in %s %s", name, method, path); 24 | } 25 | 26 | @Override 27 | public String getRuleCode() { 28 | return "R004"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/attributeremoved-deprecated/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | breed: 32 | type: string 33 | enum: [Dingo, Husky, Retriever, Shepherd] 34 | Pet: 35 | type: object 36 | properties: 37 | name: 38 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/anyof/propertydeleted/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | breed: 32 | type: string 33 | enum: [Dingo, Husky, Retriever, Shepherd] 34 | Pet: 35 | type: object 36 | properties: 37 | name: 38 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/oneof/propertydeleted/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | breed: 32 | type: string 33 | enum: [Dingo, Husky, Retriever, Shepherd] 34 | Pet: 35 | type: object 36 | properties: 37 | name: 38 | type: string -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/MavenRepoUrlHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class MavenRepoUrlHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setMavenRepoUrl(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.MAVEN_REPO_URL; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the Maven repository URL where the latest artifact denoted by the groupId and artifactId will be downloaded"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestParameterRequiredBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestParameterRequiredBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String attribute; 20 | 21 | @Override 22 | public String getMessage() { 23 | return format("Request parameter %s is required in %s %s", attribute, method, path); 24 | } 25 | 26 | @Override 27 | public String getRuleCode() { 28 | return "R007"; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/response/EmptyResponseIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.response; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.BreakingChange; 8 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | @ExtendWith(SpringExtension.class) 14 | public class EmptyResponseIntTest extends AbstractSwaggerBrakeIntTest { 15 | @Test 16 | public void testEmptyResponseWorks() { 17 | // given 18 | String apiPath = "swaggers/v2/response/emptyresponse/petstore.yaml"; 19 | 20 | // when 21 | Collection result = execute(apiPath, apiPath); 22 | // then 23 | assertThat(result).isEmpty(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/RequestParameterInType.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Arrays; 4 | 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @RequiredArgsConstructor 8 | public enum RequestParameterInType { 9 | QUERY("query"), 10 | PATH("path"), 11 | HEADER("header"); 12 | 13 | private final String name; 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | /** 20 | * Convers a string value into a {@link RequestParameterInType} instance. 21 | * @param name the name of the enum. 22 | * @return the {@link RequestParameterInType} instance. 23 | */ 24 | public static RequestParameterInType fromName(String name) { 25 | return Arrays.stream(RequestParameterInType.values()) 26 | .filter(t -> name.equals(t.getName())) 27 | .findAny() 28 | .orElseThrow(() -> new IllegalArgumentException("Name " + name + " cannot be resolved")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestTypeAttributeRemovedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestTypeAttributeRemovedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String attributeName; 20 | 21 | @Override 22 | public String getMessage() { 23 | return format("%s was removed from request in %s %s ", attributeName, method, path); 24 | } 25 | 26 | @Override 27 | public String getRuleCode() { 28 | return "R009"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/response/ResponseTypeEnumValueDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.response; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class ResponseTypeEnumValueDeletedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String enumValue; 20 | 21 | @Override 22 | public String getMessage() { 23 | return format("Enum value %s has been deleted in %s %s", enumValue, method, path); 24 | } 25 | 26 | @Override 27 | public String getRuleCode() { 28 | return "R016"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/LatestJarArtifactDownloader.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import java.io.File; 4 | 5 | import com.docktape.swagger.brake.maven.DownloadOptions; 6 | import lombok.RequiredArgsConstructor; 7 | import org.apache.http.client.methods.HttpUriRequest; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | class LatestJarArtifactDownloader { 13 | private final Maven2UrlFactory urlFactory; 14 | private final TemporaryJarFileDownloader temporaryJarFileDownloader; 15 | private final RepositoryRequestFactory requestFactory; 16 | 17 | File download(DownloadOptions options, String latestFilename, String latestVersion) { 18 | String url = urlFactory.createLatestArtifactUrl(options, latestFilename, latestVersion); 19 | HttpUriRequest httpRequest = requestFactory.create(url, options); 20 | return temporaryJarFileDownloader.download(options, httpRequest); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/integration-tests.yml: -------------------------------------------------------------------------------- 1 | name: Integration tests - Corretto JDK 17 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-24.04 14 | 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 18 | - name: Set up JDK 21 19 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5 20 | with: 21 | java-version: '21' 22 | distribution: 'corretto' 23 | cache: gradle 24 | - name: Build & Test 25 | run: | 26 | ./gradlew --no-daemon --console=plain build test 27 | 28 | - name: Archive test results 29 | if: always() 30 | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 31 | with: 32 | name: test-results 33 | path: | 34 | swagger-brake-integration-tests/build/reports/ -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/attributeremoved/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | Pet: 37 | type: object 38 | properties: 39 | name: 40 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestTypeEnumValueDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestTypeEnumValueDeletedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String enumValue; 20 | 21 | @Override 22 | public String getMessage() { 23 | return format("Enum value %s has been deleted in request body in %s %s", enumValue, method, path); 24 | } 25 | 26 | @Override 27 | public String getRuleCode() { 28 | return "R011"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/CurrentArtifactVersionHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class CurrentArtifactVersionHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setCurrentArtifactVersion(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.CURRENT_ARTIFACT_VERSION; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the current artifact version against the latest artifact resolution. Example: 1.0.0, 1.0.0-SNAPSHOT"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/validation/requestparam/path_swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "basePath": "/", 4 | "paths": { 5 | "/file/external": { 6 | "post": { 7 | "tags": [ 8 | "file-controller" 9 | ], 10 | "summary": "saveUrlFile", 11 | "operationId": "saveUrlFileUsingPOST", 12 | "consumes": [ 13 | "multipart/form-data" 14 | ], 15 | "produces": [ 16 | "application/json" 17 | ], 18 | "parameters": [ 19 | { 20 | "in": "path", 21 | "name": "access_token", 22 | "description": "access_token", 23 | "required": false, 24 | "schema": { 25 | "type": "string" 26 | } 27 | } 28 | ], 29 | "responses": { 30 | "200": { 31 | "description": "OK", 32 | "schema": { 33 | "type": "string" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/validation/requestparam/query_swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "basePath": "/", 4 | "paths": { 5 | "/file/external": { 6 | "post": { 7 | "tags": [ 8 | "file-controller" 9 | ], 10 | "summary": "saveUrlFile", 11 | "operationId": "saveUrlFileUsingPOST", 12 | "consumes": [ 13 | "multipart/form-data" 14 | ], 15 | "produces": [ 16 | "application/json" 17 | ], 18 | "parameters": [ 19 | { 20 | "in": "query", 21 | "name": "access_token", 22 | "description": "access_token", 23 | "required": false, 24 | "schema": { 25 | "type": "string" 26 | } 27 | } 28 | ], 29 | "responses": { 30 | "200": { 31 | "description": "OK", 32 | "schema": { 33 | "type": "string" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/file/FileWriter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report.file; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.Collections; 8 | 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Slf4j 13 | @Component 14 | public class FileWriter { 15 | /** 16 | * Writes the content to a file identified by filePath. 17 | * @param filePath the path to the file. 18 | * @param content the content to be written into the file. 19 | */ 20 | public void write(String filePath, String content) { 21 | try { 22 | log.debug("File path {}", filePath); 23 | Path path = Paths.get(filePath); 24 | Files.write(path, Collections.singleton(content)); 25 | } catch (IOException e) { 26 | throw new RuntimeException("Error happened while writing the output file", e); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/validation/requestparam/header_swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "basePath": "/", 4 | "paths": { 5 | "/file/external": { 6 | "post": { 7 | "tags": [ 8 | "file-controller" 9 | ], 10 | "summary": "saveUrlFile", 11 | "operationId": "saveUrlFileUsingPOST", 12 | "consumes": [ 13 | "multipart/form-data" 14 | ], 15 | "produces": [ 16 | "application/json" 17 | ], 18 | "parameters": [ 19 | { 20 | "in": "header", 21 | "name": "access_token", 22 | "description": "access_token", 23 | "required": false, 24 | "schema": { 25 | "type": "string" 26 | } 27 | } 28 | ], 29 | "responses": { 30 | "200": { 31 | "description": "OK", 32 | "schema": { 33 | "type": "string" 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/allof/propertydeleted/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | allOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: string 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | Pet: 37 | type: object 38 | properties: 39 | name: 40 | type: string -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/MavenSnapshotRepoUrlHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class MavenSnapshotRepoUrlHandler implements CliOptionHandler { 10 | @Override 11 | public void handle(String optionValue, Options options) { 12 | if (StringUtils.isNotBlank(optionValue)) { 13 | options.setMavenSnapshotRepoUrl(optionValue); 14 | } 15 | } 16 | 17 | @Override 18 | public CliOption getHandledCliOption() { 19 | return CliOption.MAVEN_SNAPSHOT_REPO_URL; 20 | } 21 | 22 | @Override 23 | public String getHelpMessage() { 24 | return "Specifies the Maven snapshot repository URL where the latest artifact denoted by the groupId and artifactId will be downloaded"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/artifactory/page/ArtifactoryLoginPage.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.artifactory.page; 2 | 3 | import java.time.Duration; 4 | 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.support.FindBy; 7 | 8 | public class ArtifactoryLoginPage extends PageBase { 9 | @FindBy(className = "login-submit-button") 10 | private WebElement loginSubmitButton; 11 | 12 | @FindBy(css = "input[name='username']") 13 | private WebElement usernameInput; 14 | 15 | @FindBy(css = "input[name='password']") 16 | private WebElement passwordInput; 17 | 18 | public void waitForPageLoad() { 19 | waitUntilVisible(loginSubmitButton, Duration.ofSeconds(30)); 20 | } 21 | 22 | public void enterCredentials(String username, String password) { 23 | type(usernameInput, username); 24 | type(passwordInput, password); 25 | } 26 | 27 | public void submitCredentials() { 28 | click(loginSubmitButton); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/request/RequestParameterRefResolutionIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.request; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.BreakingChange; 8 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | @ExtendWith(SpringExtension.class) 14 | public class RequestParameterRefResolutionIntTest extends AbstractSwaggerBrakeIntTest { 15 | @Test 16 | public void testRequestParameterRefResolutionWorks() { 17 | // given 18 | String apiPath = "swaggers/v2/request/refresolution/petstore.yaml"; 19 | 20 | // when 21 | Collection result = execute(apiPath, apiPath); 22 | // then 23 | assertThat(result).isEmpty(); 24 | } 25 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/Options.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner; 2 | 3 | import java.util.Collections; 4 | import java.util.Set; 5 | 6 | import lombok.Data; 7 | 8 | @Data 9 | public class Options { 10 | private String oldApiPath; 11 | private String newApiPath; 12 | 13 | private Set outputFormats = Collections.emptySet(); 14 | private String outputFilePath; 15 | 16 | private String mavenRepoUrl; 17 | private String mavenSnapshotRepoUrl; 18 | private String groupId; 19 | private String artifactId; 20 | private String mavenRepoUsername; 21 | private String mavenRepoPassword; 22 | private String currentArtifactVersion; 23 | private ArtifactPackaging artifactPackaging; 24 | 25 | private Boolean deprecatedApiDeletionAllowed; 26 | 27 | private String apiFilename; 28 | private String betaApiExtensionName; 29 | private Set excludedPaths = Collections.emptySet(); 30 | private Set ignoredBreakingChangeRules = Collections.emptySet(); 31 | } 32 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/response/attributeremoved/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | tags: 21 | - pets 22 | responses: 23 | '201': 24 | description: Null response 25 | content: 26 | application/json: 27 | schema: 28 | $ref: '#/components/schemas/Dog' 29 | components: 30 | schemas: 31 | Dog: 32 | type: object 33 | properties: 34 | id: 35 | type: integer 36 | breed: 37 | type: string 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/response/ResponseTypeAttributeRemovedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.response; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class ResponseTypeAttributeRemovedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String code; 20 | private final String attributeName; 21 | 22 | @Override 23 | public String getMessage() { 24 | return format("%s was removed from response %s in %s %s ", attributeName, code, method, path); 25 | } 26 | 27 | @Override 28 | public String getRuleCode() { 29 | return "R014"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/filename/ConfigurableApiFileNameChecker.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar.filename; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.core.Ordered; 5 | 6 | class ConfigurableApiFileNameChecker implements ApiFileNameChecker { 7 | private final String apiFilename; 8 | 9 | public ConfigurableApiFileNameChecker(String apiFilename) { 10 | if (StringUtils.isBlank(apiFilename)) { 11 | throw new IllegalArgumentException("apiFilename must not be empty"); 12 | } 13 | this.apiFilename = apiFilename; 14 | } 15 | 16 | @Override 17 | public boolean isApiFile(String fileName) { 18 | return fileName.endsWith(apiFilename) 19 | || fileName.endsWith(apiFilename + ".yaml") 20 | || fileName.endsWith(apiFilename + ".yml") 21 | || fileName.endsWith(apiFilename + ".json"); 22 | } 23 | 24 | @Override 25 | public int getOrder() { 26 | return Ordered.HIGHEST_PRECEDENCE; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/attributeremoved-deprecated/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | deprecated: true 34 | breed: 35 | type: string 36 | enum: [Dingo, Husky, Retriever, Shepherd] 37 | Pet: 38 | type: object 39 | properties: 40 | name: 41 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestMediaTypeDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import com.docktape.swagger.brake.core.model.MediaType; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.Getter; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.ToString; 12 | 13 | @Getter 14 | @RequiredArgsConstructor 15 | @EqualsAndHashCode 16 | @ToString 17 | public class RequestMediaTypeDeletedBreakingChange implements BreakingChange { 18 | private final String path; 19 | private final HttpMethod method; 20 | private final MediaType mediaType; 21 | 22 | @Override 23 | public String getMessage() { 24 | return format("%s media type request was removed from %s %s", mediaType, method, path); 25 | } 26 | 27 | @Override 28 | public String getRuleCode() { 29 | return "R003"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/response/ResponseMediaTypeDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.response; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import com.docktape.swagger.brake.core.model.MediaType; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.Getter; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.ToString; 12 | 13 | @Getter 14 | @RequiredArgsConstructor 15 | @EqualsAndHashCode 16 | @ToString 17 | public class ResponseMediaTypeDeletedBreakingChange implements BreakingChange { 18 | private final String path; 19 | private final HttpMethod method; 20 | private final MediaType mediaType; 21 | 22 | @Override 23 | public String getMessage() { 24 | return format("%s media type request was removed from %s %s", mediaType, method, path); 25 | } 26 | 27 | @Override 28 | public String getRuleCode() { 29 | return "R013"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/MavenConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 5 | import com.docktape.swagger.brake.maven.http.HttpClientErrorHandler; 6 | import org.apache.http.client.HttpClient; 7 | import org.apache.http.impl.client.HttpClientBuilder; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @ComponentScan 14 | public class MavenConfiguration { 15 | @Bean 16 | public HttpClient httpClient() { 17 | return HttpClientBuilder.create().addInterceptorLast(new HttpClientErrorHandler()).build(); 18 | } 19 | 20 | @Bean 21 | public XmlMapper xmlMapper() { 22 | XmlMapper xmlMapper = new XmlMapper(); 23 | xmlMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 24 | return xmlMapper; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/anyof/propertydeleted/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | required: 37 | - id 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/oneof/propertydeleted/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | required: 37 | - id 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestParameterEnumValueDeletedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestParameterEnumValueDeletedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String name; 20 | private final String enumValue; 21 | 22 | @Override 23 | public String getMessage() { 24 | return format("Enum value %s has been deleted for parameter %s in %s %s", enumValue, name, method, path); 25 | } 26 | 27 | @Override 28 | public String getRuleCode() { 29 | return "R005"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v3/schema/EmptySchemaIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v3.schema; 2 | 3 | import com.docktape.swagger.brake.core.BreakingChange; 4 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 5 | import java.util.Collection; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.springframework.test.context.junit.jupiter.SpringExtension; 10 | 11 | @ExtendWith(SpringExtension.class) 12 | public class EmptySchemaIntTest extends AbstractSwaggerBrakeIntTest { 13 | @Test 14 | public void testV3EmptySchemaElementWorks() { 15 | // given 16 | String oldApiPath = "swaggers/v3/schema/empty-schema/swagger_3.1.json"; 17 | String newApiPath = "swaggers/v3/schema/empty-schema/swagger_3.2.json"; 18 | // when 19 | Collection result = execute(oldApiPath, newApiPath); 20 | // then 21 | assertThat(result).hasSize(0); 22 | } 23 | } -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/anyof/propertytypechanged/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | required: 37 | - id 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/oneof/propertytypechanged/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | required: 37 | - id 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/anyof/propertytypechanged/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: string 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | required: 37 | - id 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/oneof/propertytypechanged/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: string 33 | breed: 34 | type: string 35 | enum: [Dingo, Husky, Retriever, Shepherd] 36 | required: 37 | - id 38 | Pet: 39 | type: object 40 | properties: 41 | name: 42 | type: string -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/DeprecatedApiDeletionAllowedHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.apache.commons.lang3.BooleanUtils; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class DeprecatedApiDeletionAllowedHandler implements CliOptionHandler { 11 | @Override 12 | public void handle(String optionValue, Options options) { 13 | if (StringUtils.isNotBlank(optionValue)) { 14 | options.setDeprecatedApiDeletionAllowed(BooleanUtils.toBooleanObject(optionValue)); 15 | } 16 | } 17 | 18 | @Override 19 | public CliOption getHandledCliOption() { 20 | return CliOption.DEPRECATED_API_DELETION_ALLOWED; 21 | } 22 | 23 | @Override 24 | public String getHelpMessage() { 25 | return "Whether to allow the deletion of deprecated APIs. Defaults to true."; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/StringSchema.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Set; 4 | 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | @Getter 10 | @EqualsAndHashCode(callSuper = true) 11 | @ToString 12 | public class StringSchema extends Schema { 13 | private final Integer maxLength; 14 | private final Integer minLength; 15 | 16 | /** 17 | * Constructs a string schema. 18 | * @param type the type 19 | * @param enumValues the enum values 20 | * @param schemaAttributes the attributes 21 | * @param schema the underlying schema 22 | * @param maxLength the maxLength constraint 23 | * @param minLength the minLength constraint 24 | */ 25 | public StringSchema(String type, Set enumValues, Set schemaAttributes, Schema schema, Integer maxLength, Integer minLength) { 26 | super(type, enumValues, schemaAttributes, schema); 27 | this.maxLength = maxLength; 28 | this.minLength = minLength; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestTypeChangedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestTypeChangedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String attributeName; 20 | private final String fromType; 21 | private final String toType; 22 | 23 | @Override 24 | public String getMessage() { 25 | return format("%s type was changed in %s %s from %s to %s ", attributeName, method, path, fromType, toType); 26 | } 27 | 28 | @Override 29 | public String getRuleCode() { 30 | return "R010"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v3/request/requestbody/memory/MemoryIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v3.request.requestbody.memory; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.BreakingChange; 8 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | @ExtendWith(SpringExtension.class) 14 | public class MemoryIntTest extends AbstractSwaggerBrakeIntTest { 15 | @Test 16 | public void testHugeFineractApi() { 17 | // given 18 | String oldApiPath = "swaggers/v3/memory/fineract/swagger_3.1.json"; 19 | String newApiPath = "swaggers/v3/memory/fineract/swagger_3.2.json"; 20 | // when 21 | Collection result = execute(oldApiPath, newApiPath); 22 | // then 23 | assertThat(result).hasSize(0); 24 | } 25 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestParameterInTypeChangedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestParameterInTypeChangedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String name; 20 | private final String oldType; 21 | private final String newType; 22 | 23 | @Override 24 | public String getMessage() { 25 | return format("%s parameter in type has been changed in %s %s from %s to %s", name, method, path, oldType, newType); 26 | } 27 | 28 | @Override 29 | public String getRuleCode() { 30 | return "R006"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /swagger-brake-cli/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | dependencies { 7 | classpath("com.gradleup.shadow:shadow-gradle-plugin:9.3.0") 8 | } 9 | } 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | apply plugin: 'com.gradleup.shadow' 16 | 17 | dependencies { 18 | implementation project(":swagger-brake") 19 | implementation "ch.qos.logback:logback-core:1.5.18" 20 | implementation "ch.qos.logback:logback-classic:1.5.18" 21 | testImplementation 'org.junit.jupiter:junit-jupiter:6.0.1' 22 | testRuntimeOnly 'org.junit.platform:junit-platform-launcher' 23 | } 24 | 25 | jar { 26 | enabled = true 27 | manifest { 28 | attributes 'Main-Class': 'com.docktape.swagger.brake.cli.SwaggerBrakeMain' 29 | } 30 | } 31 | 32 | shadowJar { 33 | archiveBaseName = 'swagger-brake' 34 | archiveClassifier = 'cli' 35 | } 36 | 37 | project.tasks.assemble.dependsOn project.tasks.shadowJar 38 | 39 | tasks.named('test', Test) { 40 | useJUnitPlatform() 41 | 42 | testLogging { 43 | events "passed" 44 | } 45 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/http/HttpClientErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.http; 2 | 3 | import java.io.IOException; 4 | 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.http.HttpException; 7 | import org.apache.http.HttpResponse; 8 | import org.apache.http.HttpResponseInterceptor; 9 | import org.apache.http.HttpStatus; 10 | import org.apache.http.protocol.HttpContext; 11 | import org.apache.http.util.EntityUtils; 12 | 13 | @Slf4j 14 | public class HttpClientErrorHandler implements HttpResponseInterceptor { 15 | @Override 16 | public void process(HttpResponse response, HttpContext context) throws HttpException, IOException { 17 | int statusCode = response.getStatusLine().getStatusCode(); 18 | if (HttpStatus.SC_UNAUTHORIZED == statusCode) { 19 | if (log.isDebugEnabled()) { 20 | String body = EntityUtils.toString(response.getEntity()); 21 | log.debug("Unauthorized access with response\n{}", body); 22 | } 23 | throw new UnauthorizedException("Request unauthorized"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/allof/propertydeleted/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | allOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: string 33 | bark: 34 | type: integer 35 | breed: 36 | type: string 37 | enum: [Dingo, Husky, Retriever, Shepherd] 38 | Pet: 39 | type: object 40 | properties: 41 | id: 42 | type: integer 43 | name: 44 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/response/attributeremoved-deprecated/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | tags: 21 | - pets 22 | responses: 23 | '201': 24 | description: Null response 25 | content: 26 | application/json: 27 | schema: 28 | $ref: '#/components/schemas/Dog' 29 | components: 30 | schemas: 31 | Dog: 32 | type: object 33 | properties: 34 | id: 35 | type: integer 36 | deprecated: true 37 | breed: 38 | type: string 39 | deprecated: true 40 | Pet: 41 | type: object 42 | properties: 43 | name: 44 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/nobreakingchange/refrequestbody/schema.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: EXAMPLE 4 | version: '1' 5 | paths: 6 | /actuator/info: 7 | get: 8 | tags: 9 | - operation-handler 10 | summary: handle 11 | operationId: handleUsingGET_1 12 | responses: 13 | '200': 14 | description: OK 15 | content: 16 | application/json: 17 | schema: 18 | type: object 19 | application/vnd.spring-boot.actuator.v2+json: 20 | schema: 21 | type: object 22 | '401': 23 | description: Unauthorized 24 | '403': 25 | description: Forbidden 26 | '404': 27 | description: Not Found 28 | deprecated: false 29 | requestBody: 30 | $ref: '#/components/requestBodies/handleUsingGETBody' 31 | components: 32 | requestBodies: 33 | handleUsingGETBody: 34 | content: 35 | application/json: 36 | schema: 37 | type: object 38 | additionalProperties: 39 | type: string 40 | description: body -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/anyof/nobreakingchange/sameattribute/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | anyOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | tags: 22 | - pets 23 | responses: 24 | '201': 25 | description: Null response 26 | components: 27 | schemas: 28 | Dog: 29 | type: object 30 | properties: 31 | id: 32 | type: integer 33 | bark: 34 | type: integer 35 | breed: 36 | type: string 37 | enum: [Dingo, Husky, Retriever, Shepherd] 38 | Pet: 39 | type: object 40 | properties: 41 | id: 42 | type: string 43 | name: 44 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/nobreakingchange/refrequestbody/schema_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: EXAMPLE 4 | version: '1' 5 | paths: 6 | /actuator/info: 7 | get: 8 | tags: 9 | - operation-handler 10 | summary: handle 11 | operationId: handleUsingGET_1 12 | responses: 13 | '200': 14 | description: OK 15 | content: 16 | application/json: 17 | schema: 18 | type: object 19 | application/vnd.spring-boot.actuator.v2+json: 20 | schema: 21 | type: object 22 | '401': 23 | description: Unauthorized 24 | '403': 25 | description: Forbidden 26 | '404': 27 | description: Not Found 28 | deprecated: false 29 | requestBody: 30 | $ref: '#/components/requestBodies/handleUsingGETBody' 31 | components: 32 | requestBodies: 33 | handleUsingGETBody: 34 | content: 35 | application/json: 36 | schema: 37 | type: object 38 | additionalProperties: 39 | type: string 40 | description: body -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/nobreakingchange/RefRequestBodyNoBreakingIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.nobreakingchange; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.BreakingChange; 8 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | @ExtendWith(SpringExtension.class) 14 | public class RefRequestBodyNoBreakingIntTest extends AbstractSwaggerBrakeIntTest { 15 | @Test 16 | public void testNoBreakingChangeWorksCorrectly() { 17 | // given 18 | String oldApiPath = "swaggers/v2/nobreakingchange/refrequestbody/schema.yaml"; 19 | String newApiPath = "swaggers/v2/nobreakingchange/refrequestbody/schema_v2.yaml"; 20 | // when 21 | Collection result = execute(oldApiPath, newApiPath); 22 | // then 23 | assertThat(result).isEmpty(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/response/ResponseTypeChangedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.response; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class ResponseTypeChangedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String code; 20 | private final String attribute; 21 | private final String oldType; 22 | private final String newType; 23 | 24 | @Override 25 | public String getMessage() { 26 | return format("Response type was changed for response %s in %s %s at attribute %s from %s to %s", code, method, path, attribute, oldType, newType); 27 | } 28 | 29 | @Override 30 | public String getRuleCode() { 31 | return "R015"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/request/OptionalRequestParameterAddedIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.request; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.BreakingChange; 8 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | 14 | @ExtendWith(SpringExtension.class) 15 | public class OptionalRequestParameterAddedIntTest extends AbstractSwaggerBrakeIntTest { 16 | @Test 17 | public void testOptionalRequestParameterAddedWorksCorrectly() { 18 | // given 19 | String oldApiPath = "swaggers/v2/request/optionalparameteradded/swagger_v1.yaml"; 20 | String newApiPath = "swaggers/v2/request/optionalparameteradded/swagger_v2.yaml"; 21 | // when 22 | Collection result = execute(oldApiPath, newApiPath); 23 | // then 24 | assertThat(result).isEmpty(); 25 | } 26 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/RequestParameterTypeChangedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestParameterTypeChangedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String name; 20 | private final String attributeName; 21 | private final String oldType; 22 | private final String newType; 23 | 24 | @Override 25 | public String getMessage() { 26 | return format("%s parameter type has been changed in %s %s at attribute %s from %s to %s", name, method, path, attributeName, oldType, newType); 27 | } 28 | 29 | @Override 30 | public String getRuleCode() { 31 | return "R008"; 32 | } 33 | } -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/request/optionalparameteradded/swagger_v1.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | description: ABC 4 | version: 1.0.5 5 | title: Swagger Petstore 6 | termsOfService: 'http://swagger.io/terms/' 7 | host: petstore.swagger.io 8 | basePath: /v2 9 | schemes: 10 | - https 11 | - http 12 | paths: 13 | /pet/uploadImage: 14 | put: 15 | tags: 16 | - pet 17 | summary: Update an existing pet 18 | description: '' 19 | operationId: updatePet 20 | parameters: 21 | - in: body 22 | name: body 23 | description: Pet object that needs to be added to the store 24 | required: true 25 | schema: 26 | $ref: '#/definitions/Pet' 27 | responses: 28 | '400': 29 | description: Invalid ID supplied 30 | definitions: 31 | Pet: 32 | type: object 33 | required: 34 | - id 35 | - breed 36 | properties: 37 | id: 38 | type: integer 39 | format: int64 40 | breed: 41 | $ref: '#/definitions/Breed' 42 | Breed: 43 | type: object 44 | required: 45 | - name 46 | properties: 47 | name: 48 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/transformer/RequestBodyTransformer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.transformer; 2 | 3 | import static java.util.stream.Collectors.toMap; 4 | 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | import com.docktape.swagger.brake.core.model.MediaType; 9 | import com.docktape.swagger.brake.core.model.Request; 10 | import com.docktape.swagger.brake.core.model.Schema; 11 | import io.swagger.v3.oas.models.parameters.RequestBody; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | public class RequestBodyTransformer implements Transformer { 18 | private final MediaTypeTransformer mediaTypeTransformer; 19 | 20 | @Override 21 | public Request transform(RequestBody from) { 22 | Set> entries = from.getContent().entrySet(); 23 | Map mediaTypes = entries.stream().collect(toMap(e -> new MediaType(e.getKey()), e -> mediaTypeTransformer.transform(e.getValue()))); 24 | return new Request(mediaTypes); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/CompositeReporter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report; 2 | 3 | import com.docktape.swagger.brake.core.ApiInfo; 4 | import java.util.Collection; 5 | 6 | import com.docktape.swagger.brake.core.BreakingChange; 7 | import com.docktape.swagger.brake.runner.Options; 8 | import lombok.RequiredArgsConstructor; 9 | 10 | @RequiredArgsConstructor 11 | class CompositeReporter implements Reporter { 12 | private final Collection delegates; 13 | 14 | @Override 15 | public void report(Collection breakingChanges, Options options) { 16 | delegates.forEach(d -> d.report(breakingChanges, options)); 17 | } 18 | 19 | @Override 20 | public void report(Collection breakingChanges, Options options, ApiInfo apiInfo) { 21 | delegates.forEach(d -> d.report(breakingChanges, options, apiInfo)); 22 | } 23 | 24 | @Override 25 | public void report(Collection breakingChanges, Collection ignoredBreakingChanges, Options options, ApiInfo apiInfo) { 26 | delegates.forEach(d -> d.report(breakingChanges, ignoredBreakingChanges, options, apiInfo)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/maven/maven2/ArtifactVersionDeciderTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertFalse; 4 | import static org.junit.jupiter.api.Assertions.assertTrue; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | public class ArtifactVersionDeciderTest { 9 | @Test 10 | public void testIsSnapshotReturnsTrueWhenVersionIsEndingWithSnapshot() { 11 | // given 12 | // when 13 | boolean result = ArtifactVersionDecider.isSnapshot("1.0.0-SNAPSHOT"); 14 | // then 15 | assertTrue(result); 16 | } 17 | 18 | @Test 19 | public void testIsSnapshotReturnsFalseWhenVersionIsNotEndingWithSnapshot() { 20 | // given 21 | // when 22 | boolean result = ArtifactVersionDecider.isSnapshot("1.0.0"); 23 | // then 24 | assertFalse(result); 25 | } 26 | 27 | @Test 28 | public void testIsSnapshotReturnsFalseWhenVersionIsCompletelyRandom() { 29 | // given 30 | // when 31 | boolean result = ArtifactVersionDecider.isSnapshot("something-else-that-is-not-a-version"); 32 | // then 33 | assertFalse(result); 34 | } 35 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/parameter/RequestParameter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.parameter; 2 | 3 | import java.util.Optional; 4 | 5 | import com.docktape.swagger.brake.core.model.AttributeType; 6 | import com.docktape.swagger.brake.core.model.RequestParameterInType; 7 | import com.docktape.swagger.brake.core.model.Schema; 8 | import lombok.AllArgsConstructor; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.Getter; 11 | import lombok.ToString; 12 | import lombok.experimental.SuperBuilder; 13 | 14 | @Getter 15 | @EqualsAndHashCode 16 | @ToString 17 | @AllArgsConstructor 18 | @SuperBuilder 19 | public class RequestParameter { 20 | private final RequestParameterInType inType; 21 | private final String name; 22 | private final boolean required; 23 | private final Schema schema; 24 | private final AttributeType requestParameterType; 25 | 26 | public RequestParameter(RequestParameterInType inType, String name, boolean required, AttributeType requestParameterType) { 27 | this(inType, name, required, null, requestParameterType); 28 | } 29 | 30 | public Optional getSchema() { 31 | return Optional.ofNullable(schema); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/SchemaStore.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | import io.swagger.v3.oas.models.media.Schema; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.function.Supplier; 8 | import lombok.RequiredArgsConstructor; 9 | 10 | @RequiredArgsConstructor 11 | public class SchemaStore { 12 | private final Map nativeSchemas; 13 | private final Map transformerSchemas = new ConcurrentHashMap<>(); 14 | 15 | public Optional getNative(String name) { 16 | return Optional.ofNullable(nativeSchemas.get(name)); 17 | } 18 | 19 | public Optional getTransformer(String name, Supplier provider) { 20 | if (!transformerSchemas.containsKey(name)) { 21 | com.docktape.swagger.brake.core.model.Schema schema = provider.get(); 22 | transformerSchemas.put(name, schema); 23 | } 24 | 25 | return Optional.ofNullable(transformerSchemas.get(name)); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/artifactory/page/ArtifactoryMainPage.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.artifactory.page; 2 | 3 | import java.util.List; 4 | 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.support.FindBy; 7 | 8 | public class ArtifactoryMainPage extends PageBase { 9 | @FindBy(css = ".icons img[alt='admin']") 10 | private WebElement adminIcon; 11 | 12 | @FindBy(css = ".vsm--link.vsm--link_level-1") 13 | private List settingsButtons; 14 | 15 | @FindBy(css = "a.vsm--link.vsm--link_level-2") 16 | private List subRepositoriesButtons; 17 | 18 | public void openAdminPanel() { 19 | click(adminIcon); 20 | } 21 | 22 | public ArtifactoryRepositoriesPage openRepositoriesSettings() { 23 | click(getRepositoriesButton()); 24 | click(getSubRepositoriesButton()); 25 | return proceed(ArtifactoryRepositoriesPage.class); 26 | } 27 | 28 | private WebElement getRepositoriesButton() { 29 | return settingsButtons.get(0); 30 | } 31 | 32 | private WebElement getSubRepositoriesButton() { 33 | return subRepositoriesButtons.get(0); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/ArraySchema.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Set; 4 | 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | @Getter 10 | @EqualsAndHashCode(callSuper = true) 11 | @ToString 12 | public class ArraySchema extends Schema { 13 | private final Integer maxItems; 14 | private final Integer minItems; 15 | private final Boolean uniqueItems; 16 | 17 | /** 18 | * Constructs an array schema. 19 | * @param type the type 20 | * @param enumValues the enum values 21 | * @param schemaAttributes the attributes 22 | * @param schema the underlying schema 23 | * @param maxItems the maxItems constraint 24 | * @param minItems the minItems constraint 25 | * @param uniqueItems the uniqueItems constraint 26 | */ 27 | public ArraySchema(String type, Set enumValues, Set schemaAttributes, Schema schema, Integer maxItems, Integer minItems, Boolean uniqueItems) { 28 | super(type, enumValues, schemaAttributes, schema); 29 | this.maxItems = maxItems; 30 | this.minItems = minItems; 31 | this.uniqueItems = uniqueItems; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/CheckerOptionsProvider.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class CheckerOptionsProvider { 7 | private CheckerOptions checkerOptions; 8 | 9 | /** 10 | * Sets the {@link CheckerOptions} instance for the current execution. 11 | * @param options the {@link CheckerOptions} instance. Must be not null. 12 | * @throws IllegalArgumentException if the provided {@link CheckerOptions} is null. 13 | */ 14 | public void set(CheckerOptions options) { 15 | if (options == null) { 16 | throw new IllegalArgumentException("options cannot be null"); 17 | } 18 | checkerOptions = options; 19 | } 20 | 21 | /** 22 | * Returns the {@link CheckerOptions} from the context. 23 | * @return the {@link CheckerOptions} if set, exception otherwise. 24 | * @throws RuntimeException if no {@link CheckerOptions} has been set. 25 | */ 26 | public CheckerOptions get() { 27 | if (checkerOptions == null) { 28 | throw new RuntimeException("CheckerOptions has not been set up yet."); 29 | } 30 | return checkerOptions; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/download/DownloadOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner.download; 2 | 3 | import com.docktape.swagger.brake.maven.DownloadOptions; 4 | import com.docktape.swagger.brake.runner.Options; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class DownloadOptionsFactory { 9 | /** 10 | * Creates a specific artifact download related option parameter. 11 | * @param options the {@link Options} instance to start from. 12 | * @return the {@link DownloadOptions} instance. 13 | */ 14 | public DownloadOptions create(Options options) { 15 | DownloadOptions result = new DownloadOptions(); 16 | result.setRepoUrl(options.getMavenRepoUrl()); 17 | result.setSnapshotRepoUrl(options.getMavenSnapshotRepoUrl()); 18 | result.setGroupId(options.getGroupId()); 19 | result.setArtifactId(options.getArtifactId()); 20 | result.setUsername(options.getMavenRepoUsername()); 21 | result.setPassword(options.getMavenRepoPassword()); 22 | result.setCurrentArtifactVersion(options.getCurrentArtifactVersion()); 23 | result.setArtifactPackaging(options.getArtifactPackaging()); 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/assets/js/31.d14e9dcd.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[31],{311:function(t,e,o){"use strict";o.r(e);var r=o(14),n=Object(r.a)({},(function(){var t=this,e=t._self._c;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"troubleshooting"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#troubleshooting"}},[t._v("#")]),t._v(" Troubleshooting")]),t._v(" "),e("h2",{attrs:{id:"cli-file-reporting-doesn-t-work"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cli-file-reporting-doesn-t-work"}},[t._v("#")]),t._v(" CLI file reporting doesn't work")]),t._v(" "),e("p",[t._v("If you encounter the following message during an execution")]),t._v(" "),e("div",{staticClass:"language-text extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[t._v("No file reporting has been done since output file path is not set\n")])])]),e("p",[t._v("It means you have configured file typed reporting like JSON or HTML but you did not pass the\n"),e("code",[t._v("--output-path")]),t._v(" argument so swagger-brake doesn't know where to store the reports.")]),t._v(" "),e("p",[t._v("Relevant section: "),e("RouterLink",{attrs:{to:"/cli/cli-interface.html#customizing-reporting"}},[t._v("Customizing reporting")])],1)])}),[],!1,null,null,null);e.default=n.exports}}]); -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/request/parameter/constraint/RequestParameterConstraintChangedBreakingChange.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule.request.parameter.constraint; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.core.BreakingChange; 6 | import com.docktape.swagger.brake.core.model.HttpMethod; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class RequestParameterConstraintChangedBreakingChange implements BreakingChange { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final String attributeName; 20 | private final ConstraintChange constraintChange; 21 | 22 | @Override 23 | public String getMessage() { 24 | return format("The %s constraint for attribute %s was changed in %s %s from %s to %s ", constraintChange.getAttribute(), 25 | attributeName, method, path, constraintChange.getOldValue(), constraintChange.getNewValue()); 26 | } 27 | 28 | @Override 29 | public String getRuleCode() { 30 | return "R017"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/filename/ApiFilenameCheckerFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar.filename; 2 | 3 | import java.util.Collection; 4 | import java.util.TreeSet; 5 | 6 | import lombok.RequiredArgsConstructor; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.core.OrderComparator; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | public class ApiFilenameCheckerFactory { 14 | /** 15 | * Creates an {@link ApiFileNameChecker} instance based on the provided configuredApiFilename argument. 16 | * @param configuredApiFilename the filename that is used to construct a proper instance f {@link ApiFileNameChecker} 17 | * @return the {@link ApiFileNameChecker} 18 | */ 19 | public ApiFileNameChecker create(String configuredApiFilename) { 20 | Collection checkers = new TreeSet<>(OrderComparator.INSTANCE); 21 | checkers.add(new SwaggerApiFileNameChecker()); 22 | if (StringUtils.isNotBlank(configuredApiFilename)) { 23 | checkers.add(new ConfigurableApiFileNameChecker(configuredApiFilename)); 24 | } 25 | return new ApiFileNameCheckerChain(checkers); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/allof/propertytypechanged/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | allOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | - type: object 22 | properties: 23 | id: 24 | type: boolean 25 | tags: 26 | - pets 27 | responses: 28 | '201': 29 | description: Null response 30 | components: 31 | schemas: 32 | Dog: 33 | type: object 34 | properties: 35 | id: 36 | type: integer 37 | bark: 38 | type: integer 39 | breed: 40 | type: string 41 | enum: [Dingo, Husky, Retriever, Shepherd] 42 | Pet: 43 | type: object 44 | properties: 45 | id: 46 | type: string 47 | name: 48 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/allof/propertytypechanged/petstore_v2.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | allOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | - type: object 22 | properties: 23 | id: 24 | type: number 25 | tags: 26 | - pets 27 | responses: 28 | '201': 29 | description: Null response 30 | components: 31 | schemas: 32 | Dog: 33 | type: object 34 | properties: 35 | id: 36 | type: integer 37 | bark: 38 | type: string 39 | breed: 40 | type: string 41 | enum: [Dingo, Husky, Retriever, Shepherd] 42 | Pet: 43 | type: object 44 | properties: 45 | id: 46 | type: object 47 | name: 48 | type: string -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/html/MustacheContentResolver.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report.html; 2 | 3 | import java.io.IOException; 4 | import java.io.StringWriter; 5 | import java.util.Map; 6 | 7 | import com.github.mustachejava.DefaultMustacheFactory; 8 | import com.github.mustachejava.Mustache; 9 | import com.github.mustachejava.MustacheFactory; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class MustacheContentResolver { 14 | /** 15 | * Resolves a mustache template into a String based on the parameter map. 16 | * @param mustacheFile the path of the mustache template. 17 | * @param paramMap the parameter map for the mustache template. 18 | * @return the resolved content. 19 | */ 20 | public String resolve(String mustacheFile, Map paramMap) { 21 | try { 22 | MustacheFactory mf = new DefaultMustacheFactory(); 23 | Mustache m = mf.compile(mustacheFile); 24 | StringWriter sw = new StringWriter(); 25 | m.execute(sw, paramMap).flush(); 26 | return sw.toString(); 27 | } catch (IOException e) { 28 | throw new RuntimeException("Error while resolving mustache content", e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | "group:allNonMajor" 6 | ], 7 | "ignorePaths": [ 8 | "docs/**", 9 | "swagger-brake-integration-tests/src/test/resources/example-projects/**" 10 | ], 11 | "packageRules": [ 12 | { 13 | "matchPackageNames": [ 14 | "io.swagger.parser.v3:swagger-parser" 15 | ], 16 | "allowedVersions": "<=2.0.5" 17 | }, 18 | { 19 | "matchPackageNames": [ 20 | "org.testng:testng" 21 | ], 22 | "allowedVersions": "<=7.5" 23 | }, 24 | { 25 | "matchPackageNames": [ 26 | "ch.qos.logback:logback-core" 27 | ], 28 | "allowedVersions": "<=1.2.11" 29 | }, 30 | { 31 | "matchPackageNames": [ 32 | "ch.qos.logback:logback-classic" 33 | ], 34 | "allowedVersions": "<=1.2.11" 35 | }, 36 | { 37 | "matchPackageNames": [ 38 | "com.fasterxml.jackson.core:jackson-databind", 39 | "com.fasterxml.jackson.dataformat:jackson-dataformat-xml" 40 | ], 41 | "allowedVersions": "<=2.13.4" 42 | }, 43 | { 44 | "matchPackageNames": [ 45 | "amazoncorretto" 46 | ], 47 | "allowedVersions": "<18" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/Path.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Collection; 4 | import java.util.Optional; 5 | 6 | import com.docktape.swagger.brake.core.model.parameter.RequestParameter; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class Path { 17 | private final String path; 18 | private final HttpMethod method; 19 | private final Request requestBody; 20 | private final Collection requestParameters; 21 | private final Collection responses; 22 | private final boolean deprecated; 23 | private final boolean betaApi; 24 | 25 | public Optional getResponseByCode(String code) { 26 | return responses.stream().filter(r -> code.equals(r.getCode())).findAny(); 27 | } 28 | 29 | public Optional getRequestParameterByName(String name) { 30 | return requestParameters.stream().filter(p -> name.equals(p.getName())).findAny(); 31 | } 32 | 33 | public Optional getRequestBody() { 34 | return Optional.ofNullable(requestBody); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v3/request/requestbody/allof/nobreakingchange/sameattribute/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pet: 11 | patch: 12 | summary: Create a pet 13 | operationId: createPets 14 | requestBody: 15 | content: 16 | application/json: 17 | schema: 18 | allOf: 19 | - $ref: '#/components/schemas/Pet' 20 | - $ref: '#/components/schemas/Dog' 21 | - $ref: '#/components/schemas/Cat' 22 | tags: 23 | - pets 24 | responses: 25 | '201': 26 | description: Null response 27 | components: 28 | schemas: 29 | Dog: 30 | type: object 31 | properties: 32 | id: 33 | type: integer 34 | bark: 35 | type: integer 36 | breed: 37 | type: string 38 | enum: [Dingo, Husky, Retriever, Shepherd] 39 | Pet: 40 | type: object 41 | properties: 42 | id: 43 | type: string 44 | name: 45 | type: string 46 | Cat: 47 | type: object 48 | properties: 49 | id: 50 | type: integer -------------------------------------------------------------------------------- /swagger-brake-integration-tests/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | } 4 | 5 | dependencies { 6 | testImplementation "ch.qos.logback:logback-core:1.5.18" 7 | testImplementation "ch.qos.logback:logback-classic:1.5.18" 8 | testImplementation "org.awaitility:awaitility:4.3.0" 9 | testImplementation "org.seleniumhq.selenium:selenium-java:4.39.0" 10 | testImplementation "org.seleniumhq.selenium:selenium-chrome-driver:4.39.0" 11 | testImplementation "io.github.bonigarcia:webdrivermanager:6.3.3" 12 | testImplementation "org.testcontainers:testcontainers:2.0.3" 13 | testImplementation "org.apache.httpcomponents:httpclient:4.5.14" 14 | testImplementation "org.projectlombok:lombok:1.18.42" 15 | testAnnotationProcessor "org.projectlombok:lombok:1.18.42" 16 | testImplementation "org.testng:testng:7.5.1" 17 | testImplementation "org.assertj:assertj-core:3.27.6" 18 | testImplementation "org.springframework:spring-test:6.2.15" 19 | testImplementation "org.mockito:mockito-core:5.21.0" 20 | testImplementation "org.apache.maven.shared:maven-invoker:3.3.0" 21 | testImplementation "com.google.code.gson:gson:2.13.2" 22 | 23 | testImplementation project(":swagger-brake-cli") 24 | } 25 | 26 | test { 27 | useTestNG() { 28 | suites 'src/test/resources/testng.xml' 29 | } 30 | testLogging { 31 | events "passed", "skipped", "failed" 32 | } 33 | } -------------------------------------------------------------------------------- /swagger-brake/src/test/resources/swaggers/v2/nobreakingchange/recursive2/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "test case", 5 | "description": "test case", 6 | "version": "unspecified" 7 | }, 8 | "basePath": "/v2", 9 | "schemes": [ 10 | "http", 11 | "https" 12 | ], 13 | "produces": [ 14 | "application/json" 15 | ], 16 | "consumes": [ 17 | "application/json" 18 | ], 19 | "paths": { 20 | "/foo": { 21 | "get": { 22 | "operationId": "getFoo", 23 | "responses": { 24 | "200": { 25 | "description": "getFoo description", 26 | "schema": { 27 | "$ref": "#/definitions/Foo" 28 | } 29 | } 30 | } 31 | } 32 | } 33 | }, 34 | "definitions": { 35 | "Foo": { 36 | "description": "Foo", 37 | "type": "object", 38 | "properties": { 39 | "foo": { 40 | "type": "object", 41 | "properties": { 42 | "condition": { 43 | "$ref": "#/definitions/Foo" 44 | } 45 | } 46 | }, 47 | "bar": { 48 | "type": "object", 49 | "properties": { 50 | "baz": { 51 | "type": "string" 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/CliOptionsValidator.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options; 2 | 3 | import static com.docktape.swagger.brake.cli.options.CliOption.*; 4 | 5 | import com.docktape.swagger.brake.runner.OptionsValidator; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class CliOptionsValidator extends OptionsValidator { 10 | @Override 11 | protected String getMavenRepoUrlName() { 12 | return MAVEN_REPO_URL.asCliOption(); 13 | } 14 | 15 | @Override 16 | protected String getMavenSnapshotRepoUrlName() { 17 | return MAVEN_SNAPSHOT_REPO_URL.asCliOption(); 18 | } 19 | 20 | @Override 21 | protected String getCurrentArtifactVersionName() { 22 | return CURRENT_ARTIFACT_VERSION.asCliOption(); 23 | } 24 | 25 | @Override 26 | protected String getGroupIdName() { 27 | return GROUP_ID.asCliOption(); 28 | } 29 | 30 | @Override 31 | protected String getArtifactIdName() { 32 | return ARTIFACT_ID.asCliOption(); 33 | } 34 | 35 | @Override 36 | protected String getOldApiPathName() { 37 | return OLD_API_PATH.asCliOption(); 38 | } 39 | 40 | @Override 41 | protected String getNewApiPathName() { 42 | return NEW_API_PATH.asCliOption(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/CliOption.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | 5 | @RequiredArgsConstructor 6 | public enum CliOption { 7 | OLD_API_PATH("old-api"), 8 | NEW_API_PATH("new-api"), 9 | HELP("help"), 10 | VERBOSE("verbose"), 11 | 12 | OUTPUT_FORMATS("output-formats"), 13 | OUTPUT_PATH("output-path"), 14 | 15 | MAVEN_REPO_URL("maven-repo-url"), 16 | MAVEN_SNAPSHOT_REPO_URL("maven-snapshot-repo-url"), 17 | MAVEN_REPO_USERNAME("maven-repo-username"), 18 | MAVEN_REPO_PASSWORD("maven-repo-password"), 19 | ARTIFACT_ID("artifactId"), 20 | GROUP_ID("groupId"), 21 | CURRENT_ARTIFACT_VERSION("current-artifact-version"), 22 | ARTIFACT_PACKAGING("artifact-packaging"), 23 | 24 | DEPRECATED_API_DELETION_ALLOWED("deprecated-api-deletion-allowed"), 25 | 26 | API_FILENAME("api-filename"), 27 | BETA_API_EXTENSION_NAME("beta-api-extension-name"), 28 | EXCLUDED_PATHS("excluded-paths"), 29 | IGNORED_BREAKING_CHANGE_RULES("ignored-breaking-change-rules"); 30 | 31 | 32 | private final String cliOptionName; 33 | 34 | public String asName() { 35 | return cliOptionName; 36 | } 37 | 38 | public String asCliOption() { 39 | return "--" + cliOptionName; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/ArtifactPackagingHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import com.docktape.swagger.brake.cli.options.CliOption; 4 | import com.docktape.swagger.brake.runner.ArtifactPackaging; 5 | import com.docktape.swagger.brake.runner.Options; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class ArtifactPackagingHandler implements CliOptionHandler { 11 | @Override 12 | public void handle(String optionValue, Options options) { 13 | ArtifactPackaging artifactPackaging = ArtifactPackaging.JAR; 14 | if (StringUtils.isNotBlank(optionValue)) { 15 | String cleanedOptionValue = optionValue.trim().toLowerCase(); 16 | artifactPackaging = ArtifactPackaging.forPackaging(cleanedOptionValue); 17 | } 18 | options.setArtifactPackaging(artifactPackaging); 19 | } 20 | 21 | @Override 22 | public CliOption getHandledCliOption() { 23 | return CliOption.ARTIFACT_PACKAGING; 24 | } 25 | 26 | @Override 27 | public String getHelpMessage() { 28 | return "Specifies the artifact packaging. Could be jar or war. Used when resolving the latest artifact version. Defaults to jar if not specified."; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src-docs/src/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const { description } = require('../../package') 2 | 3 | module.exports = { 4 | base: '/swagger-brake/', 5 | dest: '../docs', 6 | title: 'Swagger Brake', 7 | description: description, 8 | head: [ 9 | ['meta', { name: 'theme-color', content: '#3eaf7c' }], 10 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 11 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }] 12 | ], 13 | themeConfig: { 14 | repo: 'docktape/swagger-brake', 15 | editLinks: true, 16 | docsDir: 'src-docs/src', 17 | editLinkText: 'Help improve these docs!', 18 | lastUpdated: true, 19 | nav: [ 20 | { 21 | text: 'Author\'s blog', 22 | link: 'https://arnoldgalovics.com/', 23 | }, 24 | { 25 | text: 'Twitter', 26 | link: 'https://twitter.com/ArnoldGalovics', 27 | } 28 | ], 29 | sidebar: [ 30 | '/', 31 | '/configuration/', 32 | '/cli/', 33 | '/maven/', 34 | '/gradle/', 35 | '/rules/', 36 | '/constraints/', 37 | '/troubleshooting/', 38 | '/devguide/', 39 | '/changelog/', 40 | ] 41 | }, 42 | plugins: [ 43 | '@vuepress/plugin-back-to-top', 44 | '@vuepress/plugin-medium-zoom', 45 | ['@vuepress/plugin-google-analytics', { ga: "UA-78900346-2" }], 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/RepositoryRequestFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import com.docktape.swagger.brake.maven.DownloadOptions; 4 | import com.docktape.swagger.brake.maven.http.HttpRequestFactory; 5 | import lombok.RequiredArgsConstructor; 6 | import org.apache.http.client.methods.HttpUriRequest; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @RequiredArgsConstructor 11 | public class RepositoryRequestFactory { 12 | private final HttpRequestFactory requestFactory; 13 | 14 | /** 15 | * Creates an {@link HttpUriRequest} based on the parameters given. Either a Basic authorized one or 16 | * without authorization. 17 | * @param url the URL. 18 | * @param options the {@link DownloadOptions}. 19 | * @return the {@link HttpUriRequest}. 20 | */ 21 | public HttpUriRequest create(String url, DownloadOptions options) { 22 | try { 23 | if (options.isAuthenticationNeeded()) { 24 | return requestFactory.authenticatedGet(url, options.getUsername(), options.getPassword()); 25 | } else { 26 | return requestFactory.get(url); 27 | } 28 | } catch (Exception e) { 29 | throw new RuntimeException("Error while creating the http request", e); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/store/StoreProvider.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.store; 2 | 3 | public class StoreProvider { 4 | private static final ThreadLocal schemaStore = new ThreadLocal<>(); 5 | private static final ThreadLocal parameterStore = new ThreadLocal<>(); 6 | private static final ThreadLocal responseStore = new ThreadLocal<>(); 7 | 8 | public static void setSchemaStore(SchemaStore store) { 9 | schemaStore.set(store); 10 | } 11 | 12 | public static void setParameterStore(ParameterStore store) { 13 | parameterStore.set(store); 14 | } 15 | 16 | public static void setResponseStore(ResponseStore store) { 17 | responseStore.set(store); 18 | } 19 | 20 | public static SchemaStore provideSchemaStore() { 21 | return schemaStore.get(); 22 | } 23 | 24 | public static ParameterStore provideParameterStore() { 25 | return parameterStore.get(); 26 | } 27 | 28 | public static ResponseStore provideResponseStore() { 29 | return responseStore.get(); 30 | } 31 | 32 | /** 33 | * Clears all the threadlocal stores. 34 | */ 35 | public static void clear() { 36 | schemaStore.remove(); 37 | parameterStore.remove(); 38 | responseStore.remove(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/SwaggerBrakeMain.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli; 2 | 3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 4 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 5 | import org.springframework.core.env.ConfigurableEnvironment; 6 | import org.springframework.core.env.SimpleCommandLinePropertySource; 7 | 8 | @SuppressFBWarnings("DM_EXIT") 9 | public class SwaggerBrakeMain { 10 | /** 11 | * The main entrypoint for the CLI interface. 12 | * @param args the arguments 13 | */ 14 | public static void main(String[] args) { 15 | Cli cli = createCliInterface(args); 16 | int exitCode = cli.start(); 17 | if (exitCode > 0) { 18 | System.exit(exitCode); 19 | } 20 | } 21 | 22 | /** 23 | * Constructs the CLI interface. 24 | * @param args the arguments 25 | * @return the CLI interface object 26 | */ 27 | public static Cli createCliInterface(String[] args) { 28 | AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CliConfiguration.class); 29 | ConfigurableEnvironment environment = context.getEnvironment(); 30 | environment.getPropertySources().addFirst(new SimpleCommandLinePropertySource(args)); 31 | return context.getBean(Cli.class); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/Maven2LatestArtifactDownloader.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import java.io.File; 4 | 5 | import com.docktape.swagger.brake.maven.DownloadOptions; 6 | import com.docktape.swagger.brake.maven.LatestArtifactDownloader; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component("maven2LatestArtifactDownloader") 11 | @RequiredArgsConstructor 12 | class Maven2LatestArtifactDownloader implements LatestArtifactDownloader { 13 | private final LatestArtifactVersionResolver latestArtifactVersionResolver; 14 | private final LatestArtifactNameResolver latestArtifactNameResolver; 15 | private final LatestJarArtifactDownloader latestJarArtifactDownloader; 16 | 17 | @Override 18 | public File download(DownloadOptions options) { 19 | String latestVersion = latestArtifactVersionResolver.resolve(options); 20 | String latestFilename; 21 | if (ArtifactVersionDecider.isSnapshot(latestVersion)) { 22 | latestFilename = latestArtifactNameResolver.resolveSnapshot(options, latestVersion); 23 | } else { 24 | latestFilename = latestArtifactNameResolver.resolveRelease(options, latestVersion); 25 | } 26 | return latestJarArtifactDownloader.download(options, latestFilename, latestVersion); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/main/java/com/docktape/swagger/brake/cli/options/handler/ExcludedPathsHandler.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options.handler; 2 | 3 | import static java.util.stream.Collectors.toSet; 4 | 5 | import java.util.Arrays; 6 | 7 | import com.docktape.swagger.brake.cli.options.CliOption; 8 | import com.docktape.swagger.brake.runner.Options; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | @Slf4j 15 | public class ExcludedPathsHandler implements CliOptionHandler { 16 | @Override 17 | public void handle(String optionValue, Options options) { 18 | if (StringUtils.isNotBlank(optionValue)) { 19 | log.debug("Handling {} parameter with value {}", getHandledCliOption(), optionValue); 20 | String[] excludedPaths = optionValue.split(","); 21 | log.debug("Splitted values are {}", excludedPaths); 22 | options.setExcludedPaths(Arrays.stream(excludedPaths).collect(toSet())); 23 | } 24 | 25 | } 26 | 27 | @Override 28 | public CliOption getHandledCliOption() { 29 | return CliOption.EXCLUDED_PATHS; 30 | } 31 | 32 | @Override 33 | public String getHelpMessage() { 34 | return "Specifies paths that should be excluded from the check. Multiple values can be provided by using comma."; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/report/json/JsonConverter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.report.json; 2 | 3 | import java.util.Map; 4 | 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.core.type.TypeReference; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | public class JsonConverter { 14 | private final ObjectMapper objectMapper; 15 | 16 | /** 17 | * Convers an object to a JSON string representation. 18 | * @param obj the object to be converted. Must be not null. 19 | * @return the JSON representation of the object as a string. 20 | */ 21 | public String convert(Object obj) { 22 | try { 23 | return objectMapper.writeValueAsString(obj); 24 | } catch (JsonProcessingException e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | /** 30 | * Convers an object to a {@link Map} representation. 31 | * @param obj the object to be converted. Must be not null. 32 | * @return the {@link Map} representation of the object. 33 | */ 34 | public Map toMap(Object obj) { 35 | return objectMapper.convertValue(obj, new TypeReference>() {}); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/Request.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Map; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class Request { 17 | private final Map mediaTypes; 18 | 19 | public Optional getSchemaByMediaType(MediaType mediaType) { 20 | return Optional.ofNullable(mediaTypes.get(mediaType)); 21 | } 22 | 23 | /** 24 | * Checks if a specific media type is allowed by this {@link Request} instance. If the {@link MediaType#ALL} is 25 | * present within the {@link Request}, it automatically considers the given media type as allowed. 26 | * @param mediaType the {@link MediaType} 27 | * @return true if the {@link MediaType} is present or if amongst the media types, {@link MediaType#ALL} is present. 28 | * false otherwise. 29 | */ 30 | public boolean isMediaTypeAllowed(MediaType mediaType) { 31 | Set availableMediaTypes = mediaTypes.keySet(); 32 | if (availableMediaTypes.contains(MediaType.ALL)) { 33 | return true; 34 | } 35 | return availableMediaTypes.contains(mediaType); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/jar/JarScanner.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.jar; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.List; 6 | import java.util.Optional; 7 | import java.util.function.Predicate; 8 | import java.util.jar.JarEntry; 9 | import java.util.jar.JarFile; 10 | 11 | import org.apache.commons.collections4.EnumerationUtils; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | public class JarScanner { 16 | /** 17 | * Scans a JAR file for a single entry based on the predicate given. 18 | * @param jarFile the {@link File} object pointing to the JAR file. 19 | * @param criteria the predicate that the entries will be matched against. 20 | * @return An {@link Optional} {@link JarEntry} that matched the predicate given 21 | * @throws IOException if an I/O error has occurred 22 | */ 23 | public Optional find(File jarFile, Predicate criteria) throws IOException { 24 | Optional result = Optional.empty(); 25 | try (JarFile jar = new JarFile(jarFile)) { 26 | List jarEntries = EnumerationUtils.toList(jar.entries()); 27 | for (JarEntry entry : jarEntries) { 28 | if (criteria.test(entry)) { 29 | result = Optional.of(entry); 30 | } 31 | } 32 | } 33 | return result; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/rule/PathSkipper.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.rule; 2 | 3 | import java.util.Set; 4 | 5 | import com.docktape.swagger.brake.core.CheckerOptions; 6 | import com.docktape.swagger.brake.core.CheckerOptionsProvider; 7 | import com.docktape.swagger.brake.core.model.Path; 8 | import com.docktape.swagger.brake.core.util.PathNormalizer; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | @RequiredArgsConstructor 14 | public class PathSkipper { 15 | private final CheckerOptionsProvider checkerOptionsProvider; 16 | 17 | public boolean shouldSkip(Path path) { 18 | return path.isBetaApi() || isPathExcluded(path); 19 | } 20 | 21 | private boolean isPathExcluded(Path path) { 22 | CheckerOptions checkerOptions = checkerOptionsProvider.get(); 23 | if (checkerOptions != null) { 24 | Set excludedPaths = checkerOptions.getExcludedPaths(); 25 | for (String excludedPath : excludedPaths) { 26 | String normalizedExcludedPath = PathNormalizer.normalizePathSlashes(excludedPath); 27 | String normalizedPath = PathNormalizer.normalizePathSlashes(path.getPath()); 28 | if (normalizedPath.startsWith(normalizedExcludedPath)) { 29 | return true; 30 | } 31 | } 32 | } 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/nobreakingchange/IgnoredBreakingChangeIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.nobreakingchange; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.google.common.collect.Sets; 6 | import com.docktape.swagger.brake.core.BreakingChange; 7 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 8 | import com.docktape.swagger.brake.runner.Options; 9 | import java.util.Collection; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.springframework.test.context.junit.jupiter.SpringExtension; 13 | 14 | @ExtendWith(SpringExtension.class) 15 | public class IgnoredBreakingChangeIntTest extends AbstractSwaggerBrakeIntTest { 16 | @Test 17 | public void testIgnoreBreakingChangeRulesWorksCorrectlyForExactMatch() { 18 | // given 19 | String oldApiPath = "swaggers/v2/request/parameterdeleted/petstore.yaml"; 20 | String newApiPath = "swaggers/v2/request/parameterdeleted/petstore_v2.yaml"; 21 | Options options = new Options(); 22 | options.setOldApiPath(oldApiPath); 23 | options.setNewApiPath(newApiPath); 24 | options.setIgnoredBreakingChangeRules(Sets.newHashSet("R004", "R001")); 25 | // when 26 | Collection result = execute(options); 27 | // then 28 | assertThat(result).isEmpty(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/Response.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.util.Map; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.ToString; 11 | 12 | @Getter 13 | @RequiredArgsConstructor 14 | @EqualsAndHashCode 15 | @ToString 16 | public class Response { 17 | private final String code; 18 | private final Map mediaTypes; 19 | 20 | public Optional getSchemaByMediaType(MediaType mediaType) { 21 | return Optional.ofNullable(mediaTypes.get(mediaType)); 22 | } 23 | 24 | /** 25 | * Checks if a specific media type is allowed by this {@link Response} instance. If the {@link MediaType#ALL} is 26 | * present within the {@link Response}, it automatically considers the given media type as allowed. 27 | * @param mediaType the {@link MediaType} 28 | * @return true if the {@link MediaType} is present or if amongst the media types, {@link MediaType#ALL} is present. 29 | * false otherwise. 30 | */ 31 | public boolean isMediaTypeAllowed(MediaType mediaType) { 32 | Set availableMediaTypes = mediaTypes.keySet(); 33 | if (availableMediaTypes.contains(MediaType.ALL)) { 34 | return true; 35 | } 36 | return availableMediaTypes.contains(mediaType); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/TemporaryJarFileDownloader.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | 7 | import com.docktape.swagger.brake.maven.DownloadOptions; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.io.FileUtils; 11 | import org.apache.http.HttpResponse; 12 | import org.apache.http.client.HttpClient; 13 | import org.apache.http.client.methods.HttpUriRequest; 14 | import org.springframework.stereotype.Component; 15 | 16 | @Component 17 | @RequiredArgsConstructor 18 | @Slf4j 19 | class TemporaryJarFileDownloader { 20 | private final HttpClient httpClient; 21 | 22 | File download(DownloadOptions options, HttpUriRequest httpRequest) { 23 | try { 24 | log.debug("Downloading artifact from {}", httpRequest.getURI()); 25 | File destination = Files.createTempFile("swagger-brake", "." + options.getArtifactPackaging().getPackaging()).toFile(); 26 | HttpResponse response = httpClient.execute(httpRequest); 27 | FileUtils.copyInputStreamToFile(response.getEntity().getContent(), destination); 28 | log.debug("Created temporary artifact file to {}", destination.getAbsolutePath()); 29 | return destination; 30 | } catch (IOException e) { 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/plugin/PluginProjectContainerBase.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project.plugin; 2 | 3 | import com.docktape.swagger.brake.integration.project.ProjectContainerBase; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.containers.Network; 7 | import org.testcontainers.images.builder.ImageFromDockerfile; 8 | 9 | @Slf4j 10 | public abstract class PluginProjectContainerBase extends ProjectContainerBase { 11 | protected PluginProjectContainerBase(Network network, T parameter) { 12 | super(network, parameter); 13 | } 14 | 15 | @Override 16 | protected ImageFromDockerfile containerImage(PluginProjectParameter parameter) { 17 | return new ImageFromDockerfile() 18 | .withFileFromClasspath("/", getProjectResourcePath()) 19 | .withFileFromClasspath("/src/main/resources/swagger.yaml", parameter.getClasspathSwagger()); 20 | } 21 | 22 | @Override 23 | protected GenericContainer configureContainer(GenericContainer container, PluginProjectParameter parameter) { 24 | return container 25 | .withEnv("SWAGGER_BRAKE_VERSION", parameter.getSwaggerBrakeVersion().getVersion()) 26 | .withEnv("ARTIFACT_ID", parameter.getArtifactId()); 27 | } 28 | 29 | protected abstract String getProjectResourcePath(); 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake-cli/src/test/java/com/docktape/swagger/brake/cli/options/CliOptionsProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.cli.options; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.catchThrowable; 5 | import static org.mockito.BDDMockito.given; 6 | import static org.mockito.BDDMockito.then; 7 | 8 | import java.util.List; 9 | 10 | import com.docktape.swagger.brake.cli.options.handler.CliOptionHandler; 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import org.mockito.InjectMocks; 14 | import org.mockito.Mock; 15 | import org.mockito.junit.jupiter.MockitoExtension; 16 | import org.springframework.core.env.Environment; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | public class CliOptionsProviderTest { 20 | @Mock 21 | private List handlers; 22 | @Mock 23 | private CliHelpProvider helpProvider; 24 | @Mock 25 | private Environment environment; 26 | 27 | @InjectMocks 28 | private CliOptionsProvider underTest; 29 | 30 | @Test 31 | public void testProvideShouldThrowExceptionWhenHelpIsRequired() { 32 | // given 33 | given(environment.getProperty("help")).willReturn(""); 34 | // when 35 | Throwable exception = catchThrowable(() -> underTest.provide()); 36 | // then 37 | assertThat(exception).isInstanceOf(CliHelpException.class); 38 | then(helpProvider).should().getHelp(); 39 | } 40 | } -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/core/model/transformer/ApiResponseTransformerTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.transformer; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import com.docktape.swagger.brake.core.model.Response; 6 | import io.swagger.v3.oas.models.responses.ApiResponse; 7 | import org.apache.commons.lang3.tuple.ImmutablePair; 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.InjectMocks; 11 | import org.mockito.Mock; 12 | import org.mockito.junit.jupiter.MockitoExtension; 13 | 14 | @ExtendWith(MockitoExtension.class) 15 | public class ApiResponseTransformerTest { 16 | @Mock 17 | private MediaTypeTransformer mediaTypeTransformer; 18 | 19 | @InjectMocks 20 | private ApiResponseTransformer underTest; 21 | 22 | /* 23 | Totally valid case when the schema only says that the response is 401 and provides only a description without any schema. 24 | */ 25 | @Test 26 | public void testTransformShouldNotFailWhenFromContentIsNull() { 27 | // given 28 | String code = "401"; 29 | ApiResponse apiResponse = new ApiResponse(); 30 | apiResponse.setDescription("Unauthorized"); 31 | // when 32 | Response result = underTest.transform(new ImmutablePair<>(code, apiResponse)); 33 | // then 34 | assertThat(result).isNotNull(); 35 | assertThat(result.getCode()).isEqualTo(code); 36 | } 37 | } -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/CheckerOptionsFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner; 2 | 3 | import com.docktape.swagger.brake.core.CheckerOptions; 4 | import org.apache.commons.lang3.BooleanUtils; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class CheckerOptionsFactory { 10 | /** 11 | * Converts the {@link Options} instance into a {@link CheckerOptions} instance. 12 | * @param options the {@link Options} to be converted. 13 | * @return the {@link CheckerOptions} instance. 14 | */ 15 | public CheckerOptions create(Options options) { 16 | CheckerOptions checkerOptions = new CheckerOptions(); 17 | checkerOptions.setDeprecatedApiDeletionAllowed(isDeprecatedApiDeletionAllowed(options)); 18 | checkerOptions.setBetaApiExtensionName(getBetaApiExtensionName(checkerOptions.getBetaApiExtensionName(), options)); 19 | checkerOptions.setExcludedPaths(options.getExcludedPaths()); 20 | return checkerOptions; 21 | } 22 | 23 | private String getBetaApiExtensionName(String defaultBetaApiExtensionName, Options options) { 24 | return StringUtils.defaultString(options.getBetaApiExtensionName(), defaultBetaApiExtensionName); 25 | } 26 | 27 | private boolean isDeprecatedApiDeletionAllowed(Options options) { 28 | return BooleanUtils.toBooleanDefaultIfNull(options.getDeprecatedApiDeletionAllowed(), true); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/MavenMetadataDownloader.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 7 | import com.docktape.swagger.brake.maven.model.MavenMetadata; 8 | import lombok.RequiredArgsConstructor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.http.HttpResponse; 11 | import org.apache.http.client.HttpClient; 12 | import org.apache.http.client.methods.HttpUriRequest; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | @Slf4j 18 | class MavenMetadataDownloader { 19 | private final HttpClient httpClient; 20 | private final XmlMapper xmlMapper; 21 | 22 | MavenMetadata download(HttpUriRequest httpRequest) { 23 | try { 24 | log.debug("Downloading maven metadata from {}", httpRequest.getURI()); 25 | HttpResponse response = httpClient.execute(httpRequest); 26 | // TODO: content type check would be great here 27 | return getMetadata(response); 28 | } catch (IOException e) { 29 | throw new RuntimeException("Cannot get metadata", e); 30 | } 31 | } 32 | 33 | private MavenMetadata getMetadata(HttpResponse response) throws IOException { 34 | InputStream content = response.getEntity().getContent(); 35 | return xmlMapper.readValue(content, MavenMetadata.class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/maven/maven2/LatestArtifactNameResolver.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.maven.maven2; 2 | 3 | import static java.lang.String.format; 4 | 5 | import com.docktape.swagger.brake.maven.DownloadOptions; 6 | import com.docktape.swagger.brake.maven.model.MavenMetadata; 7 | import com.docktape.swagger.brake.maven.model.MavenSnapshot; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | class LatestArtifactNameResolver { 14 | private final Maven2UrlFactory urlFactory; 15 | private final MavenMetadataDownloader metadataDownloader; 16 | private final RepositoryRequestFactory requestFactory; 17 | 18 | String resolveSnapshot(DownloadOptions options, String latestVersion) { 19 | String metadataUrl = urlFactory.createLatestArtifactSnapshotMetadataUrl(options, latestVersion); 20 | MavenMetadata snapshotMetadata = metadataDownloader.download(requestFactory.create(metadataUrl, options)); 21 | MavenSnapshot snapshot = snapshotMetadata.getVersioning().getSnapshot(); 22 | String snapshotVersion = latestVersion.replaceAll("SNAPSHOT", snapshot.getTimestamp()); 23 | return format("%s-%s-%s", snapshotMetadata.getArtifactId(), snapshotVersion, snapshot.getBuildNumber()); 24 | } 25 | 26 | String resolveRelease(DownloadOptions options, String latestVersion) { 27 | return format("%s-%s", options.getArtifactId(), latestVersion); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/runner/openapi/OpenApiFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.runner.openapi; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.parser.OpenAPIV3Parser; 5 | import io.swagger.v3.parser.core.models.ParseOptions; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * The class is responsible for loading an OpenAPI definition. 10 | */ 11 | @Component 12 | public class OpenApiFactory { 13 | /** 14 | * The method loads an OpenAPI definition from the file system. 15 | * @param path the path of the definition file. It can be a relative path or an absolute one as well. 16 | * @return the {@link OpenAPI} object instance representing the definition 17 | * @throws IllegalStateException in case any error happens 18 | */ 19 | public OpenAPI fromFile(String path) { 20 | try { 21 | OpenAPI loadedApi = loadV3Api(path); 22 | if (loadedApi == null) { 23 | throw new IllegalStateException("API cannot be loaded from path " + path); 24 | } 25 | return loadedApi; 26 | } catch (Exception e) { 27 | throw new IllegalStateException("API cannot be loaded from path " + path, e); 28 | } 29 | } 30 | 31 | private OpenAPI loadV3Api(String path) { 32 | ParseOptions parseOptions = new ParseOptions(); 33 | parseOptions.setResolveFully(true); 34 | return new OpenAPIV3Parser().read(path, null, parseOptions); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/NumberSchema.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Set; 5 | 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | 10 | @Getter 11 | @EqualsAndHashCode(callSuper = true) 12 | @ToString 13 | public class NumberSchema extends Schema { 14 | private final BigDecimal maximum; 15 | private final BigDecimal minimum; 16 | private final boolean exclusiveMaximum; 17 | private final boolean exclusiveMinimum; 18 | 19 | /** 20 | * Constructs a number schema. 21 | * @param type the type 22 | * @param enumValues the enum values 23 | * @param schemaAttributes the attributes 24 | * @param schema the underlying schema 25 | * @param maximum the maximum constraint 26 | * @param minimum the minimum constraint 27 | * @param exclusiveMaximum the exclusiveMaximum constraint 28 | * @param exclusiveMinimum the exclusiveMinimum constraint 29 | */ 30 | public NumberSchema(String type, Set enumValues, Set schemaAttributes, Schema schema, 31 | BigDecimal maximum, BigDecimal minimum, boolean exclusiveMaximum, boolean exclusiveMinimum) { 32 | super(type, enumValues, schemaAttributes, schema); 33 | this.maximum = maximum; 34 | this.minimum = minimum; 35 | this.exclusiveMaximum = exclusiveMaximum; 36 | this.exclusiveMinimum = exclusiveMinimum; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/project/plugin/ExampleMavenProjectContainer.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.project.plugin; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.testcontainers.containers.GenericContainer; 5 | import org.testcontainers.containers.Network; 6 | 7 | @Slf4j 8 | public class ExampleMavenProjectContainer extends PluginProjectContainerBase { 9 | public ExampleMavenProjectContainer(Network network, PluginProjectParameter parameter) { 10 | super(network, parameter); 11 | } 12 | 13 | @Override 14 | protected GenericContainer configureContainer(GenericContainer container, PluginProjectParameter parameter) { 15 | return super.configureContainer(container, parameter) 16 | .withEnv("REMOVE_PACKAGING", Boolean.toString(parameter.isRemovePackaging())); 17 | } 18 | 19 | @Override 20 | protected String getProjectResourcePath() { 21 | return "/example-projects/swagger-brake-maven-example"; 22 | } 23 | 24 | @Override 25 | protected boolean isBuildSuccess(String logs) { 26 | return logs.contains("BUILD SUCCESS"); 27 | } 28 | 29 | @Override 30 | protected boolean isBuildFailure(String logs) { 31 | return logs.contains("BUILD FAILURE"); 32 | } 33 | 34 | public boolean isFirstVersionAssumed() { 35 | return container.getLogs().contains("Assuming this is the first version of the artifact, skipping check for breaking changes"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/model/parameter/StringRequestParameter.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core.model.parameter; 2 | 3 | import com.docktape.swagger.brake.core.model.AttributeType; 4 | import com.docktape.swagger.brake.core.model.RequestParameterInType; 5 | import com.docktape.swagger.brake.core.model.Schema; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | import lombok.experimental.SuperBuilder; 10 | 11 | @Getter 12 | @EqualsAndHashCode(callSuper = true) 13 | @ToString 14 | @SuperBuilder 15 | public class StringRequestParameter extends RequestParameter { 16 | private Integer maxLength; 17 | private Integer minLength; 18 | 19 | /** 20 | * Constructor to create an instance. 21 | * @param inType the {@link RequestParameterInType} 22 | * @param name the name 23 | * @param required whether its required 24 | * @param schema the @{@link Schema} 25 | * @param requestParameterType the {@link AttributeType} 26 | * @param maxLength the maximum length 27 | * @param minLength the minimum length 28 | */ 29 | public StringRequestParameter(RequestParameterInType inType, String name, 30 | boolean required, Schema schema, AttributeType requestParameterType, 31 | Integer maxLength, Integer minLength) { 32 | super(inType, name, required, schema, requestParameterType); 33 | this.maxLength = maxLength; 34 | this.minLength = minLength; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /swagger-brake/src/main/java/com/docktape/swagger/brake/core/DefaultBreakChecker.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.core; 2 | 3 | import static java.util.stream.Collectors.toList; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.model.Specification; 8 | import com.docktape.swagger.brake.core.rule.BreakingChangeRule; 9 | import java.util.Comparator; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Component; 13 | 14 | 15 | @Component 16 | @RequiredArgsConstructor 17 | @Slf4j 18 | class DefaultBreakChecker implements BreakChecker { 19 | private final Collection> rules; 20 | 21 | @Override 22 | public Collection check(Specification oldApi, Specification newApi) { 23 | if (log.isDebugEnabled()) { 24 | rules.stream().map(BreakingChangeRule::getClass).map(Class::getName).forEach(name -> log.debug("Rule configured: {}", name)); 25 | } 26 | if (oldApi == null) { 27 | throw new IllegalArgumentException("oldApi must be provided"); 28 | } 29 | if (newApi == null) { 30 | throw new IllegalArgumentException("newApi must be provided"); 31 | } 32 | return rules.parallelStream() 33 | .map(rule -> rule.checkRule(oldApi, newApi)) 34 | .flatMap(Collection::stream) 35 | .sorted(Comparator.comparing(bc -> bc.getClass().getSimpleName())) 36 | .collect(toList()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/nobreakingchange/NoBreakingChangeIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.nobreakingchange; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | 7 | import com.docktape.swagger.brake.core.BreakingChange; 8 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.extension.ExtendWith; 11 | import org.springframework.test.context.junit.jupiter.SpringExtension; 12 | 13 | @ExtendWith(SpringExtension.class) 14 | public class NoBreakingChangeIntTest extends AbstractSwaggerBrakeIntTest { 15 | @Test 16 | public void testNoBreakingChangeWorksCorrectly() { 17 | // given 18 | String oldApiPath = "swaggers/v2/nobreakingchange/petstore.yaml"; 19 | String newApiPath = "swaggers/v2/nobreakingchange/petstore_v2.yaml"; 20 | // when 21 | Collection result = execute(oldApiPath, newApiPath); 22 | // then 23 | assertThat(result).isEmpty(); 24 | } 25 | 26 | @Test 27 | public void testNoBreakingChangeWhenSameApiUsedWorksCorrectly() { 28 | // given 29 | String oldApiPath = "swaggers/v2/nobreakingchange/petstore.yaml"; 30 | String newApiPath = "swaggers/v2/nobreakingchange/petstore.yaml"; 31 | // when 32 | Collection result = execute(oldApiPath, newApiPath); 33 | // then 34 | assertThat(result).isEmpty(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/v2/response/ResponseDeletedIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.v2.response; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Collection; 6 | import java.util.Collections; 7 | 8 | import com.docktape.swagger.brake.core.BreakingChange; 9 | import com.docktape.swagger.brake.core.model.HttpMethod; 10 | import com.docktape.swagger.brake.core.rule.response.ResponseDeletedBreakingChange; 11 | import com.docktape.swagger.brake.integration.AbstractSwaggerBrakeIntTest; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.extension.ExtendWith; 14 | import org.springframework.test.context.junit.jupiter.SpringExtension; 15 | 16 | @ExtendWith(SpringExtension.class) 17 | public class ResponseDeletedIntTest extends AbstractSwaggerBrakeIntTest { 18 | @Test 19 | public void testResponseTypeChangeIsBreakingChangeWhenExistingAttributeRemoved() { 20 | // given 21 | String oldApiPath = "swaggers/v2/response/deleted/petstore.yaml"; 22 | String newApiPath = "swaggers/v2/response/deleted/petstore_v2.yaml"; 23 | ResponseDeletedBreakingChange bc = new ResponseDeletedBreakingChange("/pet", HttpMethod.PUT, "404"); 24 | Collection expected = Collections.singleton(bc); 25 | // when 26 | Collection result = execute(oldApiPath, newApiPath); 27 | // then 28 | assertThat(result).hasSize(1); 29 | assertThat(result).hasSameElementsAs(expected); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /swagger-brake-integration-tests/src/test/java/com/docktape/swagger/brake/integration/artifactory/factory/PageFactory.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration.artifactory.factory; 2 | 3 | import java.lang.reflect.Constructor; 4 | 5 | import com.docktape.swagger.brake.integration.artifactory.page.PageBase; 6 | import lombok.RequiredArgsConstructor; 7 | import org.openqa.selenium.WebDriver; 8 | import org.springframework.util.ReflectionUtils; 9 | 10 | @RequiredArgsConstructor 11 | public class PageFactory { 12 | private final WebDriver webDriver; 13 | 14 | public T create(Class pageClazz) { 15 | try { 16 | Constructor emptyConstructor = pageClazz.getDeclaredConstructor(); 17 | emptyConstructor.setAccessible(true); 18 | T pageObject = emptyConstructor.newInstance(); 19 | setField(pageObject, webDriver); 20 | org.openqa.selenium.support.PageFactory.initElements(webDriver, pageObject); 21 | return pageObject; 22 | } catch (Exception e) { 23 | throw new RuntimeException("Cannot create page object", e); 24 | } 25 | } 26 | 27 | private void setField(T pageObject, R fieldInstance) { 28 | ReflectionUtils.doWithFields(pageObject.getClass(), field -> { 29 | if (field.getType().isAssignableFrom(fieldInstance.getClass())) { 30 | ReflectionUtils.makeAccessible(field); 31 | ReflectionUtils.setField(field, pageObject, fieldInstance); 32 | } 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /swagger-brake/src/test/java/com/docktape/swagger/brake/integration/AbstractSwaggerBrakeIntTest.java: -------------------------------------------------------------------------------- 1 | package com.docktape.swagger.brake.integration; 2 | 3 | import com.google.common.collect.ImmutableSet; 4 | import com.docktape.swagger.brake.core.BreakingChange; 5 | import com.docktape.swagger.brake.core.CoreConfiguration; 6 | import com.docktape.swagger.brake.maven.MavenConfiguration; 7 | import com.docktape.swagger.brake.report.ReporterConfiguration; 8 | import com.docktape.swagger.brake.runner.Options; 9 | import com.docktape.swagger.brake.runner.OutputFormat; 10 | import com.docktape.swagger.brake.runner.Runner; 11 | import com.docktape.swagger.brake.runner.RunnerConfiguration; 12 | import java.util.Collection; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.test.context.ContextConfiguration; 15 | 16 | @ContextConfiguration(classes = {CoreConfiguration.class, RunnerConfiguration.class, ReporterConfiguration.class, MavenConfiguration.class}) 17 | public abstract class AbstractSwaggerBrakeIntTest { 18 | @Autowired 19 | protected Runner underTest; 20 | 21 | protected Collection execute(String oldApiPath, String newApiPath) { 22 | Options options = new Options(); 23 | options.setOldApiPath(oldApiPath); 24 | options.setNewApiPath(newApiPath); 25 | return execute(options); 26 | } 27 | 28 | protected Collection execute(Options options) { 29 | options.setOutputFormats(ImmutableSet.of(OutputFormat.STDOUT)); 30 | return underTest.run(options); 31 | } 32 | } 33 | --------------------------------------------------------------------------------