Giter Site home page Giter Site logo

redskap / swagger-brake Goto Github PK

View Code? Open in Web Editor NEW
54.0 3.0 16.0 1.77 MB

Swagger contract checker for breaking API changes

License: Apache License 2.0

Java 98.78% Dockerfile 0.13% JavaScript 0.21% Stylus 0.06% Mustache 0.43% Shell 0.39%
swagger swagger-api swagger-parser api rest rest-api restful-api restful java

swagger-brake's People

Contributors

denistsoi avatar dylanwatsonsoftware avatar galovics avatar pkunze avatar renovate[bot] avatar vavvolo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

swagger-brake's Issues

Don't disable all logging

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.

Required fields in nested definitions are being reported as breaking when they are not

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.

Shouldn't removing a request body type be 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

Add route path exclusion parameter support

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'

Document usage as gradle/maven plugin

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.

Stack overflow on recursive schemas

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"
    }
  }
}

Schema class attributeRequiredMap.get(attrToSearchFor); returning null

Hello, I have a case where attributeRequiredMap.get(attrToSearchFor); is returning null in the Boolean isRequired like the following Image

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

image

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

Can you provide a simple entrypoint to compare 2 API instances

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);
}

Null output for formData parameter

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?

Support for beta API

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

Swagger brake cli jar in maven repository

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?

Exitcode for CLI when API has breaking changes

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 should return results

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?

java.lang.OutOfMemoryError upon first try

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.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

dockerfile
Dockerfile
  • openjdk 17.0.2-jdk
  • openjdk 17.0.2-jdk
github-actions
.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
gradle
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/gradle-wrapper.properties
  • gradle 8.4
npm
src-docs/package.json
  • @vuepress/plugin-google-analytics 1.9.10
  • vuepress 1.9.10

  • Check this box to trigger a request for Renovate to run again on this repository

can't run jar

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

Report all changes optionally

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

swagger-brake not reporting violation in breaking changes | MIN/MAX validation | POST object mandatory field validation

I am facing couple of issues where swagger-brake is not reporting violation in breaking changes

  1. Changed minimum/ maximum validation in new contract. please find the example below

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

  1. Changed POST object field from optional to mandatory in new contract. please find the example below

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

swagger.zip

Please look into it and let me know in case any further details are needed.

Thanks,
Madhur

Path comparison does not factor in basePath

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.

The attempt to fix recursive schemas causes NPE

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.

Add support for $ref to all types that allow reference objects

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:

  • ApiResponse
  • Parameter
  • RequestBody

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

Need null checks for getContent

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

Error when objectA contains objectA

I think Error raised when swagger contains following structure:

example

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)

Change in type of response attribute is not detected

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"
},
.............................

Performance enhancement for larger API jsons

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.

Allow configuring the filename scanned from maven

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

SNAPSHOT releases should be optional

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.