redskap / swagger-brake Goto Github PK
View Code? Open in Web Editor NEWSwagger contract checker for breaking API changes
License: Apache License 2.0
Swagger contract checker for breaking API changes
License: Apache License 2.0
I am trying to use swagger-brake as a library not from the CLI. It will actually be invoked as an integration test, because right now we have no swagger files in the code and use one generated by spring-fox at run time. That will hopefully change in the future.
But the problem is that just including swagger-brake as a library is causing some unit tests to fail. The reason is that it has a logback.xml that has:
<root level="OFF" >
which is turning off logging at the root level and I have some tests that verify that certain messages are logged.
You should only control your own logging, not all logging.
Consider these two spec files:
Old
swagger: '2.0'
info:
description: ABC
version: 1.0.5
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'
host: petstore.swagger.io
basePath: /v2
schemes:
- https
- http
paths:
/pet/uploadImage:
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
parameters:
- in: body
name: body
description: Pet object that needs to be added to the store
required: true
schema:
$ref: '#/definitions/Pet'
responses:
'400':
description: Invalid ID supplied
definitions:
Pet:
type: object
required:
- id
properties:
id:
type: integer
format: int64
Lets add a new field in the body parameter Pet which is optional but a field inside that is required.
swagger: '2.0'
info:
description: ABC
version: 1.0.5
title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/'
host: petstore.swagger.io
basePath: /v2
schemes:
- https
- http
paths:
/pet/uploadImage:
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
parameters:
- in: body
name: body
description: Pet object that needs to be added to the store
required: true
schema:
$ref: '#/definitions/Pet'
responses:
'400':
description: Invalid ID supplied
definitions:
Pet:
type: object
required:
- id
properties:
id:
type: integer
format: int64
breed:
$ref: '#/definitions/Breed'
Breed:
type: object
required:
- name
properties:
name:
type: string
This means that if Breed is passed in body then it must have name property. Or else we can send the request without the whole breed object.
This is being reported as breaking change.
java -jar /usr/local/lib/swagger-brake-cli-2.2.0.jar --old-api=old.yml --new-api=new.yaml Loading old API from old.yml Loading new API from new.yaml Successfully loaded APIs Starting the check for breaking API changes Check is finished There were breaking API changes R007 Request parameter breed.name is required in PUT /pet/uploadImage
Incase there already exist a optional parameter and we add new required fields inside that, then it qualifies as a breaking change.
Say oldAPI contains a path with:
requestBody:
content:
application/xml:
schema:
$ref: '#/components/schemas/schema1'
application/json:
schema:
$ref: '#/components/schemas/schema2'
And the new API removes one:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/schema2'
That should be a breaking change since the path no longer accepts xml
Currently, we have some Private API's in our spec. They are being broken all the time, but it doesn't bother us.
However, the swagger-brake fails, of course, because it detects breaking changes in the API.
Is it possible to add a parameter for partial path (e.g --exclude-path /api/private) which will exclude these API's from the scan? or at least WARN about them, but not mark the check as 'Not backwards compatible'
If I understood your introducing blog-post this tool can be used in during the build in gradle or maven, is this correct?
If so, please document the usage in gradle and maven.
We had our own library that tried to do what swagger-brake is doing and ran into the same problem.
The solution is that you have to remember where you have been when recursing and don't recurse into a schema you have already seen on the way down.
Here is a simplified swagger spec that causes the stack overflow. Note that PaymentItemDTO contains an array of children PaymentItemDTO objects.
{
"swagger":"2.0",
"host":"localhost",
"basePath":"/",
"tags":[
{
"name":"audit-controller",
"description":"Audit Controller"
},
{
"name":"audit-fee-controller",
"description":"Audit Fee Controller"
},
{
"name":"operation-handler",
"description":"Operation Handler"
}
],
"paths":{
"/api/v1/audits/summary/{businessId}":{
"get":{
"tags":[
"audit-controller"
],
"summary":"fetchAuditSummaryByBusinessId",
"operationId":"fetchAuditSummaryByBusinessIdUsingGET",
"produces":[
"*/*"
],
"parameters":[
{
"name":"businessId",
"in":"path",
"description":"businessId",
"required":true,
"type":"string",
"format":"uuid"
}
],
"responses":{
"200":{
"description":"OK",
"schema":{
"$ref":"#/definitions/AuditSummaryDTO"
}
},
"401":{
"description":"Unauthorized"
},
"403":{
"description":"Forbidden"
},
"404":{
"description":"Not Found"
}
},
"security":[
{
"jwt":[
"global"
]
}
],
"deprecated":false
}
}
},
"securityDefinitions":{
"jwt":{
"type":"apiKey",
"name":"Authorization",
"in":"header"
}
},
"definitions":{
"AuditSummaryDTO":{
"type":"object",
"properties":{
"payoffSavings":{
"type":"number"
},
"unverifiedPayoff":{
"type":"number"
},
"unverifiedPayoffBreakdown":{
"type":"array",
"items":{
"$ref":"#/definitions/PaymentItemDTO"
}
},
"unverifiedVehicles":{
"type":"integer",
"format":"int64"
}
},
"title":"AuditSummaryDTO"
},
"PaymentItemDTO":{
"type":"object",
"properties":{
"amountApplied":{
"type":"number"
},
"children":{
"type":"array",
"items":{
"$ref":"#/definitions/PaymentItemDTO"
}
},
"description":{
"type":"string"
},
"recordType":{
"type":"string",
"enum":[
"PRINCIPAL",
"INTEREST",
"CPP",
"TRANSPORTATION_FEE",
"AUDIT_FEE",
"OTHER_FEE"
]
}
},
"title":"PaymentItemDTO"
}
}
}
Hello, I have a case where attributeRequiredMap.get(attrToSearchFor);
is returning null in the Boolean isRequired
like the following Image
I was wondering if the isRequired
can be converted into a primitive boolean in order not to have NPE, the following is the content of the map
Also current attrToSearchFor
value is urn:scim:schemas:extension:workspace:1
the closest value in the map is urn:scim:schemas:extension:workspace:1.0
I suspect that is this something related to the construction of attrToSearchFor
using the stringJoiner.
Thank you
Since you are splitting out the CLI from the core library, was hoping you could provide a simple entry point that takes in 2 OpenAPI instances and returns the list of breaking changes.
Here is the code I am currently doing on my side, but you could do it much easier in the library:
private static Collection<BreakingChange> invokeSwaggerBrake(@NotNull OpenAPI oldApi, @NotNull OpenAPI newApi) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
RunnerConfiguration.class, ReporterConfiguration.class,
MavenConfiguration.class, CoreConfiguration.class);
final String beanName = context.getBeanNamesForType(
ResolvableType.forClassWithGenerics(Transformer.class, OpenAPI.class, Specification.class))[0];
@SuppressWarnings("unchecked")
final Transformer<OpenAPI, Specification> transformer
= (Transformer<OpenAPI, Specification>)context.getBean(beanName);
return context.getBean(BreakChecker.class).check(transformer.transform(oldApi), transformer.transform(newApi));
}
Should be easy if you extract this expression on Runner to a new method called check:
breakChecker.check(transformer.transform(oldApi), transformer.transform(newApi));
Extract everything before .run(options)
in Starter.start to a getRunner method. Then add this method to Starter:
public static Collection<BreakingChange> check(@NotNull OpenAPI oldApi, @NotNull OpenAPI newApi) {
return getRunner().check(oldApi, newApi);
}
Hi
Defining parameter as below gives null on running swagger brake:
{"in":"formData","name":"fileName","description":"fileName","required":true,"schema":{"type":"string"}}
Output:
Successfully loaded APIs
Starting the check for breaking API changes
null
For help please use --help
Is there anything missing?
Hello,
this is an idea for a feature request.
Let's assume I do have a stable 1.0 API. Now we would like to provide a new endpoint, but in the current state we are not sure if the new API will work properly. So we release the new API 1.1 and tell the user to play around. But now, if we have to change the API because of user feedback for version 1.2 swagger-brake will fail as expected.
However If we could annotate the API as beta in the first place it would be possible to identify this API and ignore any breaking changes.
The Problem is swagger doesn't support a beta tag yet. See here. As metioned in the issue it is possible to add a vendor extension
This could be something like:
x-brake-beta: true
x-brake-beta-api-name: uniqeXXX
I guess the x-brake-beta-api-name
is required to ensure the same api's are compared. But I am not sure, maybe x-brake-beta
is enough.
The following state could appear:
-> Beta API -> OK // added
Beta API -> Beta API -> OK // changed
Beta API -> API -> OK // stable
Beta API -> -> OK // removed
API -> Beta API -> FAIL // brake
kind regards
Christian
I downloaded swagger brake cli from maven repository but it is not bundled with the dependencies and hence when I run the cli it is failing. I size of latest version on GitHub page is 27+MB on /releases whereas it is 17KB on Maven repository.We are not allowed to download from /releases and are allowed only to download from Maven repository. Please let us know how we can get the swagger brake jar that is bundled with all the dependencies into it from maven repository?
Hi, thanks for a great project.
We want to run the swagger-brake CLI as a part of our deployment pipeline, like hit the breaks if the live API has breaking changes from what we were targeting when building. I'm looking for a way to make the CLI itself break the chain if a breaking changes is present, but as far as I have seen the exitcode of the CLI is 0 regardless of how the check goes. Is it some other way I should check? In my case, the best solution would be something like exitcode 1 for a There were breaking API changes
message from the API. This would be great and save us from needing wrapper-code.
Starter class looks like a way to invoke swagger-brake as a library, which is what I am trying to do instead of using the CLI. However the problem is that if you run it via Starter there is no indication of whether it passed or failed. Why not have it return the list of breaking changes?
I just tried out swagger-brake and am getting the following Exception:
java -jar .\swagger-brake-2.3.0-cli.jar --old-api=swagger_3.1.json --new-api=swagger_3.2.json --output-formats=STDOUT,JSON,HTML
Loading old API from swagger_3.1.json
Loading new API from swagger_3.2.json
Successfully loaded APIs
Starting the check for breaking API changes
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.TreeMap.addEntry(Unknown Source)
at java.base/java.util.TreeMap.put(Unknown Source)
at java.base/java.util.TreeMap.put(Unknown Source)
at java.base/java.util.TreeSet.add(Unknown Source)
at java.base/java.util.AbstractCollection.addAll(Unknown Source)
at java.base/java.util.TreeSet.addAll(Unknown Source)
at java.base/java.util.TreeSet.<init>(Unknown Source)
at io.redskap.swagger.brake.core.model.SchemaBuilder.build(SchemaBuilder.java:109)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:105)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:53)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.lambda$getSchemaAttributes$0(SchemaTransformer.java:121)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer$$Lambda$138/0x0000000800e3fda0.apply(Unknown Source)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.base/java.util.Iterator.forEachRemaining(Unknown Source)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.base/java.util.stream.ReferencePipeline.collect(Unknown Source)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.getSchemaAttributes(SchemaTransformer.java:126)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:100)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:53)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.lambda$getSchemaAttributes$0(SchemaTransformer.java:121)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer$$Lambda$138/0x0000000800e3fda0.apply(Unknown Source)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.base/java.util.Iterator.forEachRemaining(Unknown Source)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source)
at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
I would glady share both json files, but I'd rather do so privately via e-mail.
If this is relevant: we are generating the everything swagger related using swashbuckle on asp.net core 6.0.
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
gradle
, checkstyle
, org.mockito:mockito-junit-jupiter
, org.mockito:mockito-core
, org.springframework:spring-test
, org.assertj:assertj-core
, org.junit.jupiter:junit-jupiter-api
, org.junit.jupiter:junit-jupiter-engine
, org.springframework:spring-context
, org.apache.commons:commons-lang3
, org.projectlombok:lombok
, org.testcontainers:testcontainers
, io.github.bonigarcia:webdrivermanager
, org.seleniumhq.selenium:selenium-chrome-driver
, org.seleniumhq.selenium:selenium-java
, com.github.spotbugs.snom:spotbugs-gradle-plugin
, org.awaitility:awaitility
)Dockerfile
openjdk 17.0.2-jdk
openjdk 17.0.2-jdk
.github/workflows/docker-build.yml
actions/checkout v4@b4ffde65f46336ab88eb53be808477a3936bae11
ubuntu 22.04
.github/workflows/integration-tests.yml
actions/checkout v4@b4ffde65f46336ab88eb53be808477a3936bae11
actions/setup-java v3@0ab4596768b603586c0de567f2430c30f5b0d2b0
gradle/wrapper-validation-action 342dbebe7272035434f9baccc29a816ec6dd2c7b
actions/upload-artifact v3@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
ubuntu 22.04
settings.gradle
build.gradle
com.github.spotbugs.snom:spotbugs-gradle-plugin 5.2.1
org.projectlombok:lombok 1.18.30
org.projectlombok:lombok 1.18.30
com.google.guava:guava 32.1.3-jre
org.apache.commons:commons-collections4 4.4
org.apache.commons:commons-lang3 3.13.0
org.apache.commons:commons-lang3 3.13.0
org.springframework:spring-context 6.0.13
org.springframework:spring-context 6.0.13
com.fasterxml.jackson.dataformat:jackson-dataformat-xml 2.13.4
org.junit.jupiter:junit-jupiter-engine 5.10.0
org.junit.jupiter:junit-jupiter-api 5.10.0
org.assertj:assertj-core 3.24.2
org.springframework:spring-test 6.0.13
org.mockito:mockito-core 5.6.0
org.mockito:mockito-junit-jupiter 5.6.0
checkstyle 10.12.4
swagger-brake/build.gradle
org.apache.commons:commons-collections4 4.4
io.swagger.parser.v3:swagger-parser 2.0.5
com.fasterxml.jackson.core:jackson-databind 2.13.4.2
com.github.spullara.mustache.java:compiler 0.9.11
org.apache.httpcomponents:httpclient 4.5.14
swagger-brake-cli/build.gradle
com.github.johnrengelman:shadow 8.1.1
ch.qos.logback:logback-core 1.2.11
ch.qos.logback:logback-classic 1.2.11
swagger-brake-integration-tests/build.gradle
ch.qos.logback:logback-core 1.2.11
ch.qos.logback:logback-classic 1.2.11
org.awaitility:awaitility 4.2.0
org.seleniumhq.selenium:selenium-java 4.14.1
org.seleniumhq.selenium:selenium-chrome-driver 4.14.1
io.github.bonigarcia:webdrivermanager 5.6.0
org.testcontainers:testcontainers 1.19.1
org.apache.httpcomponents:httpclient 4.5.14
org.projectlombok:lombok 1.18.30
org.projectlombok:lombok 1.18.30
org.testng:testng 7.5.1
org.assertj:assertj-core 3.24.2
org.springframework:spring-test 6.0.13
org.mockito:mockito-core 5.6.0
org.apache.maven.shared:maven-invoker 3.2.0
com.google.code.gson:gson 2.10.1
gradle/wrapper/gradle-wrapper.properties
gradle 8.4
src-docs/package.json
@vuepress/plugin-google-analytics 1.9.10
vuepress 1.9.10
pardon my ignorance, but how do you actually get the executable JAR file?
I tried running from my .m2 folder but it failed
~/bin/swag > java -jar ~/.m2/repository/io/redskap/swagger-brake/0.3.0/swagger-brake-0.3.0.jar --help
no main manifest attribute, in /Users/jason.berk/.m2/repository/io/redskap/swagger-
brake/0.3.0/swagger-brake-0.3.0.jar
~/bin/swag > java -jar ~/.m2/repository/io/redskap/swagger-brake/0.2.1/swagger-brake-0.2.1.jar --help
no main manifest attribute, in /Users/jason.berk/.m2/repository/io/redskap/swagger-brake/0.2.1/swagger-brake-0.2.1.jar
Currently it shows only breaking changes. Is there a way it can report call the changes if we pass additional flags? For example, if we are adding new attributes to request body or response body it is not treating it as a breaking change but for a business scenario it might be a change that needs to be alerted. Please let me know
Add ability to allow removal of an API previously marked as deprecated
I am facing couple of issues where swagger-brake is not reporting violation in breaking changes
Old Contract
{
"name": "orderId",
"in": "path",
"description": "ID of pet that needs to be fetched",
"required": true,
"type": "integer",
"maximum": 10,
"minimum": 1,
"format": "int64"
},
New Contract
{
"name": "orderId",
"in": "path",
"description": "ID of pet that needs to be fetched",
"required": true,
"type": "integer",
"maximum": 5,
"minimum": 3,
"format": "int64"
},
Output
Loading old API from swagger-old.json
Loading new API from swagger-new.json
Successfully loaded APIs
Starting the check for breaking API changes
Check is finished
No breaking API changes detecte
Old Contract:
"Pet": {
"type": "object",
"required": [
"photoUrls"
],
New Contract:
"Pet": {
"type": "object",
"required": [
"photoUrls",
"name"
],
Output
Loading old API from swagger-old.json
Loading new API from swagger-new.json
Successfully loaded APIs
Starting the check for breaking API changes
Check is finished
No breaking API changes detected
Attached both old and new swagger json files
Please look into it and let me know in case any further details are needed.
Thanks,
Madhur
Let's say that I start with an old API that does not set the baseBath and has a path like /api/v1/foo
If in a new API I set the basePath to /bar swagger brake does not see that as a breaking change even though I have now changed all the paths.
Conversely, if I factor out a common prefix to basePath by setting basePath to /api/v1 and changing the path to foo then swagger brake reports that as a breaking change even though none of the routes changed.
Imagine some schemas in components like these where I whittle it down to only enough properties to demonstrate the problem:
Pageable:
type: object
properties:
sort:
$ref: '#/components/schemas/Sort'
title: Pageable
Sort:
type: object
properties:
sorted:
type: boolean
title: Sort
Page_Foo_:
type: object
properties:
pageable:
$ref: '#/components/schemas/Pageable'
sort:
$ref: '#/components/schemas/Sort'
This causes an NPE because the sort property $ref is not expanded, because #/components/schemas/Sort was seen when expanding Pageable.
Currently reference objects (i.e. $ref) are only supported for schema. It needs to be supported for all types that support reference objects. For the types that swagger-brake cares about that means:
Here is a file that causes an NPE due to ref for the request body:
openapi: 3.0.0
info:
title: EXAMPLE
version: '1'
paths:
/actuator/info:
get:
tags:
- operation-handler
summary: handle
operationId: handleUsingGET_1
responses:
'200':
description: OK
responseSchema:
type: object
content:
application/json:
schema:
type: object
application/vnd.spring-boot.actuator.v2+json:
schema:
type: object
'401':
description: Unauthorized
'403':
description: Forbidden
'404':
description: Not Found
deprecated: false
requestBody:
$ref: '#/components/requestBodies/handleUsingGETBody'
components:
requestBodies:
handleUsingGETBody:
content:
application/json:
schema:
type: object
additionalProperties:
type: string
description: body
Had a Swagger 2.0 file that I converted to 3.0 and when ran with your tool would fail with null pointer exceptions. These occurred in ApiResponseTransformer and RequestBodyTransformer because getContent() returned null.
I think you need some null checking like this (version for ApiResponseTransformer):
final Content content = from.getValue().getContent();
Set<Map.Entry<String, MediaType>> entries = content != null ? content.entrySet() : Collections.emptySet();
When I added the null checks to those classes the check worked as expected
I think Error raised when swagger contains following structure:
ObjectA{
List < ObjectA > childs;
}
Starting the check for breaking API changes
Exception in thread "main" java.lang.StackOverflowError
at java.lang.String.lastIndexOf(String.java:1807)
at io.redskap.swagger.brake.core.model.service.TypeRefNameResolver.getNameFromRef(TypeRefNameResolver.java:16)
at io.redskap.swagger.brake.core.model.service.TypeRefNameResolver.resolve(TypeRefNameResolver.java:9)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.getSchemaForRef(SchemaTransformer.java:76)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:44)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:35)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.lambda$getSchemaAttributes$0(SchemaTransformer.java:66)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.getSchemaAttributes(SchemaTransformer.java:69)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:49)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:45)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:35)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.lambda$getSchemaAttributes$0(SchemaTransformer.java:66)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.getSchemaAttributes(SchemaTransformer.java:69)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:49)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:45)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:35)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.lambda$getSchemaAttributes$0(SchemaTransformer.java:66)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.getSchemaAttributes(SchemaTransformer.java:69)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:49)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.transformSchema(SchemaTransformer.java:45)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:38)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.internalTransform(SchemaTransformer.java:35)
at io.redskap.swagger.brake.core.model.transformer.SchemaTransformer.lambda$getSchemaAttributes$0(SchemaTransformer.java:66)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
Message for ResponseTypeChangedBreakingChange:
Response type was change for response
Changed response attribute id from integer to string, as below, and this failing change was not detected.
From:
"Pet": {
"type": "object",
"required": [
"name",
"photoUrls"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
......
To:
"Pet": {
"type": "object",
"required": [
"name",
"photoUrls"
],
"properties": {
"id": {
"type": "string"
},
.............................
At this time the whole check runs in a single thread. For larger API jsons (300+ paths) processing takes a while.
Simple math: 300paths * count of breaking changes checks. It does not scale much.
The processing can be launched in parallel
In DefaultBreakChecker
return rules.stream().map(rule -> rule.checkRule(oldApi, newApi)).flatMap(Collection::stream).collect(toList());
with just a replacement of stream()
into parallelStream()
it runs faster...
Unfortunately it has some possible side effects. The order of result list of breaking changes is unpredictable - that can be confusing for user. Therefore I also suggest to add a support for BreakingChange priority and to sort the result list by this value. Eg. PathRemoval breaking change can have higher order priority than others.
Also I am not sure about implementation of classes SeenRefHolder, SchemaStoreProvider
which they are using ThreadLocal
.
There are also other possible places for speed improvements. Eg. loading old and new api can be launched in parallel as well. With Project reactor it can be a piece of cake.
As the documentation says:
The downloaded JAR will be scanned for any of the following files which will be used for providing the old API:
- swagger.json
- swagger.yaml
- swagger.yml
I don't want swagger used in the name, since my file is an open api file and I prefer to maintain the distinction between swagger and openApi. I am proposing a new config property to set the base name of the file to look for that defaults to "swagger". Swagger brake then looks for a file with that base name with the extension .json, .yaml, or .yml
We don't have any snapshot releases on our nexus.
That leads to this exception (with the maven plugin):
[INFO] --- swagger-brake-maven-plugin:2.0.0-SNAPSHOT:check (default) @ dummy ---
[INFO] Downloading latest artifact from repository 'http://localhost/nexus/content/groups/releases' with groupId 'my.group' artifactId 'my.artifact'
[info] Latest version of the artifact could not be retrieved from http://localhost/nexus/content/groups/releases with my.group:my.artifact
[info] Assuming this is the first version of the artifact, skipping check for breaking changes
io.redskap.swagger.brake.runner.exception.LatestArtifactDownloadException: Error while downloading the latest version of the artifact
at io.redskap.swagger.brake.runner.download.ArtifactDownloaderHandler.handle(ArtifactDownloaderHandler.java:43)
at io.redskap.swagger.brake.runner.Runner.run(Runner.java:27)
at io.redskap.swagger.brake.runner.Starter.start(Starter.java:15)
at io.redskap.swagger.brake.maven.StarterWrapper.start(StarterWrapper.java:11)
at io.redskap.swagger.brake.maven.Executor.execute(Executor.java:22)
at io.redskap.swagger.brake.maven.SwaggerBrakeMojo.execute(SwaggerBrakeMojo.java:55)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.apache.maven.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:39)
at org.apache.maven.wrapper.WrapperExecutor.execute(WrapperExecutor.java:122)
at org.apache.maven.wrapper.MavenWrapperMain.main(MavenWrapperMain.java:61)
Caused by: java.lang.RuntimeException: Cannot get metadata
at io.redskap.swagger.brake.maven.maven2.MavenMetadataDownloader.download(MavenMetadataDownloader.java:29)
at io.redskap.swagger.brake.maven.maven2.LatestSnapshotNameResolver.resolve(LatestSnapshotNameResolver.java:21)
at io.redskap.swagger.brake.maven.maven2.Maven2LatestArtifactDownloader.download(Maven2LatestArtifactDownloader.java:20)
at io.redskap.swagger.brake.runner.download.ArtifactDownloaderHandler.handle(ArtifactDownloaderHandler.java:36)
... 34 more
Caused by: javax.xml.bind.UnmarshalException: unerwartetes Element (URI:"", lokal:"html"). Erwartete Elemente sind <{}metadata>,<{}snapshot>,<{}versioning>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:714)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:232)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:227)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:94)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1119)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:544)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:526)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:138)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:510)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3063)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:836)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:534)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1216)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:228)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:199)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:170)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:209)
at io.redskap.swagger.brake.maven.maven2.MavenMetadataDownloader.getMetadata(MavenMetadataDownloader.java:35)
at io.redskap.swagger.brake.maven.maven2.MavenMetadataDownloader.download(MavenMetadataDownloader.java:27)
... 37 more
It tries to get maven-metadata.xml
within a version subfolder, however, there is none. There is only one in the top directory.
So the LatestSnapshotNameResolver should not stop the entire process.
Based on #21
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.