diff --git a/.gitignore b/.gitignore
index 618c6c87..6da7e20b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,7 @@ testDiff.md
testDeprecatedApi.html
testNewApi.html
+
+.vscode/
+
+.DS_Store
diff --git a/Example/petstore_v3_testResponseRender.html b/Example/petstore_v3_testResponseRender.html
new file mode 100644
index 00000000..021d6e31
--- /dev/null
+++ b/Example/petstore_v3_testResponseRender.html
@@ -0,0 +1 @@
+
ChangelogVersions⇑
Changes from 1.0 to 1.1.
What's New⇑
- GET/admin/item
- POST/admin/item
- GET/item
What's Changed⇑
- POST/item
Return Type
Produces
Responses
- Add response 201
- Add response 400
- Delete response
404 - Response 200
- Description change into
- Change id
- Add content-type application/xml
- Add header X-New-Header
- Delete header
X-Deprecated-Header - Change header X-Rate-Limit
\ No newline at end of file
diff --git a/Example/testDiff.json b/Example/testDiff.json
new file mode 100644
index 00000000..50933396
--- /dev/null
+++ b/Example/testDiff.json
@@ -0,0 +1 @@
+{"changedEndpoints":[{"changedOperations":{"PUT":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[{"el":"newFeild","newEnums":false,"property":{"description":"a feild demo by sayi","example":"a feild demo by sayi","exampleSetFlag":true,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"description":"a new user feild demo","exampleSetFlag":false,"extensions":{},"format":"int32","specVersion":"V30","type":"integer","types":["integer"]},"removedEnums":false,"typeChange":false}],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":false,"diffRequestProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[{"el":"category.name","newEnums":false,"property":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"removedEnums":false,"typeChange":false}],"missingResponses":{},"summary":"Update an existing pet"},"POST":{"addConsumes":[],"addParameters":[{"description":"add new query param demo","explode":true,"extensions":{},"in":"query","name":"tags","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[0].changedOperations.POST.addParameters[0].extensions"},"items":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"specVersion":"V30","type":"array","types":["array"]},"style":"FORM"}],"addProduces":[],"addProps":[],"addRequestProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"diffRequestProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingResponses":{},"summary":"Add a new pet to the store"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet"},{"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"addRequestProps":[],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[{"addContentTypes":[],"addHeaders":{},"addProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"changedHeaders":[],"changedProps":[],"contentTypeChanged":false,"descriptionChanged":false,"diff":true,"headersChanged":false,"missingContentTypes":[],"missingHeaders":{},"missingProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"newDescription":"successful operation","oldDescription":"successful operation","schemaChanged":true,"statusCode":"200"}],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":true,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingRequestProps":[],"missingResponses":{},"summary":"Finds Pets by status"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet/findByStatus"},{"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"addRequestProps":[],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[{"addContentTypes":[],"addHeaders":{},"addProps":[{"el":"newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"owner.newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"changedHeaders":[],"changedProps":[],"contentTypeChanged":false,"descriptionChanged":false,"diff":true,"headersChanged":false,"missingContentTypes":[],"missingHeaders":{},"missingProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"newDescription":"successful operation","oldDescription":"successful operation","schemaChanged":true,"statusCode":"200"}],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":true,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[{"el":"category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"owner.phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingRequestProps":[],"missingResponses":{},"summary":"Finds Pets by tags"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet/findByTags"},{"changedOperations":{"DELETE":{"addConsumes":[],"addParameters":[{"extensions":{},"in":"header","name":"newHeaderParam","schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[3].changedOperations.DELETE.addParameters[0].extensions"},"specVersion":"V30","type":"string","types":["string"]}}],"addProduces":[],"addProps":[],"addRequestProps":[],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[],"missingResponses":{},"summary":"Deletes a pet"}},"diff":true,"missingOperations":{"POST":{"extensions":{},"operationId":"updatePetWithForm","parameters":[{"description":"ID of pet that needs to be updated","extensions":{},"in":"path","name":"petId","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[3].missingOperations.POST.parameters[0].extensions"},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"exampleSetFlag":false,"schema":{"exampleSetFlag":false,"properties":{"name":{"description":"Updated name of the pet","exampleSetFlag":false,"extensions":{},"name":"name","specVersion":"V30","type":"string","types":["string"]},"status":{"description":"Updated status of the pet","exampleSetFlag":false,"extensions":{},"name":"status","specVersion":"V30","type":"string","types":["string"]}},"specVersion":"V30","type":"object","types":["object"]}}}},"responses":{"405":{"content":{},"description":"Invalid input","extensions":{}}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"summary":"Updates a pet in the store with form data","tags":["pet"]}},"newOperations":{"GET":{"description":"Returns a single pet","extensions":{},"operationId":"getPetById","parameters":[{"description":"ID of pet to return","extensions":{},"in":"path","name":"petId","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[3].newOperations.GET.parameters[0].extensions"},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]}}],"responses":{"200":{"content":{"application/xml":{"exampleSetFlag":false,"schema":{"exampleSetFlag":false,"jsonSchema":{"type":"object","required":["name","photoUrls"],"properties":{"id":{"type":"integer","format":"int64"},"category":{"$ref":"#/components/schemas/Category","originalRef":"#/components/schemas/Category"},"name":{"type":"string","example":"doggies"},"newFeild":{"type":"string","example":"a feild demo by sayi","description":"a feild demo by sayi"},"owner":{"$ref":"#/components/schemas/User","originalRef":"#/components/schemas/User"},"parent":{"$ref":"#/components/schemas/Pet","originalRef":"#/components/schemas/Pet"},"photoUrls":{"type":"array","xml":{"name":"photoUrl","wrapped":true},"items":{"type":"string"}},"tags":{"type":"array","xml":{"name":"tag","wrapped":true},"items":{"$ref":"#/components/schemas/Tag","originalRef":"#/components/schemas/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"Pet"}},"properties":{"id":{"exampleSetFlag":false,"extensions":{},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]},"category":{"exampleSetFlag":false,"jsonSchema":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"newCatFeild":{"type":"string"}},"xml":{"name":"Category"}},"properties":{"id":{"exampleSetFlag":false,"extensions":{},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]},"newCatFeild":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"}},"specVersion":"V30","type":"object","types":["object"],"xml":{"name":"Category"}},"name":{"example":"doggies","exampleSetFlag":true,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"newFeild":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"owner":{"exampleSetFlag":false,"jsonSchema":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"username":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"favorite":{"$ref":"#/components/schemas/Pet","originalRef":"#/components/schemas/Pet"},"userStatus":{"type":"integer","format":"int32","description":"User Status"},"newUserFeild":{"type":"integer","format":"int32","description":"a new user feild demo"}},"xml":{"name":"User"}},"properties":{"id":{"exampleSetFlag":false,"extensions":{},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]},"username":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"firstName":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"lastName":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"email":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"password":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"favorite":{"exampleSetFlag":false,"specVersion":"V30"},"userStatus":{"description":"User Status","exampleSetFlag":false,"extensions":{},"format":"int32","specVersion":"V30","type":"integer","types":["integer"]},"newUserFeild":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"}},"specVersion":"V30","type":"object","types":["object"],"xml":{"name":"User"}},"parent":{"$ref":"$.changedEndpoints[3].newOperations.GET.responses.200.content.application/xml.schema.properties.owner.properties.favorite"},"photoUrls":{"exampleSetFlag":false,"extensions":{},"items":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"specVersion":"V30","type":"array","types":["array"],"xml":{"name":"photoUrl","wrapped":true}},"tags":{"exampleSetFlag":false,"extensions":{},"items":{"exampleSetFlag":false,"jsonSchema":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Tag"}},"properties":{"id":{"exampleSetFlag":false,"extensions":{},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]},"name":{"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]}},"specVersion":"V30","type":"object","types":["object"],"xml":{"name":"Tag"}},"specVersion":"V30","type":"array","types":["array"],"xml":{"name":"tag","wrapped":true}},"status":{"description":"pet status in the store","enum":["available","pending","sold"],"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]}},"required":["name","photoUrls"],"specVersion":"V30","type":"object","types":["object"],"xml":{"name":"Pet"}}},"application/json":{"exampleSetFlag":false,"schema":{"$ref":"$.changedEndpoints[3].newOperations.GET.responses.200.content.application/xml.schema"}}},"description":"successful operation","extensions":{}},"400":{"content":{},"description":"Invalid ID supplied","extensions":{}},"404":{"content":{},"description":"Pet not found","extensions":{}}},"security":[{"api_key":[]}],"summary":"Find pet by ID","tags":["pet"]}},"pathUrl":"/pet/{petId}"},{"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[],"addResponses":{},"changedParameter":[{"changeDescription":true,"changeRequired":false,"changed":[],"diff":true,"increased":[],"leftParameter":{"description":"ID of pet to update","extensions":{},"in":"path","name":"petId","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[4].changedOperations.POST.changedParameter[0].leftParameter.extensions"},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]}},"missing":[],"rightParameter":{"description":"ID of pet to update, default false","extensions":{},"in":"path","name":"petId","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[4].changedOperations.POST.changedParameter[0].rightParameter.extensions"},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]}}}],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[],"missingResponses":{},"summary":"uploads an image for pet"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/pet/{petId}/uploadImage"},{"changedOperations":{"POST":{"addConsumes":["application/json"],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[],"addResponses":{},"changedParameter":[],"changedProps":[{"el":"id","newEnums":false,"property":{"exampleSetFlag":false,"extensions":{},"format":"int64","specVersion":"V30","type":"integer","types":["integer"]},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"description":"Order Status","enum":["placed","approved","delivered"],"exampleSetFlag":false,"extensions":{},"specVersion":"V30","type":"string","types":["string"]},"removedEnums":true,"typeChange":false}],"changedRequestProps":[{"el":"id","newEnums":false,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[0].property"},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[1].property"},"removedEnums":true,"typeChange":false}],"changedResponses":[{"addContentTypes":[],"addHeaders":{},"addProps":[],"changedHeaders":[],"changedProps":[{"el":"id","newEnums":false,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[0].property"},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[1].property"},"removedEnums":true,"typeChange":false}],"contentTypeChanged":true,"descriptionChanged":false,"diff":true,"headersChanged":false,"missingContentTypes":["application/xml"],"missingHeaders":{},"missingProps":[],"newDescription":"successful operation","oldDescription":"successful operation","schemaChanged":true,"statusCode":"200"}],"diff":true,"diffConsumes":true,"diffParam":false,"diffProduces":true,"diffProp":true,"diffRequestProp":true,"missingConsumes":["application/xml"],"missingParameters":[],"missingProduces":["application/xml"],"missingProps":[],"missingRequestProps":[],"missingResponses":{},"summary":"Place an order for a pet"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/store/order"},{"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[],"addResponses":{},"changedParameter":[],"changedProps":[{"el":"id","newEnums":false,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[0].property"},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[1].property"},"removedEnums":true,"typeChange":false}],"changedRequestProps":[],"changedResponses":[{"addContentTypes":[],"addHeaders":{},"addProps":[],"changedHeaders":[],"changedProps":[{"el":"id","newEnums":false,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[0].property"},"removedEnums":false,"typeChange":true},{"el":"status","newEnums":true,"property":{"$ref":"$.changedEndpoints[5].changedOperations.POST.changedProps[1].property"},"removedEnums":true,"typeChange":false}],"contentTypeChanged":false,"descriptionChanged":false,"diff":true,"headersChanged":false,"missingContentTypes":[],"missingHeaders":{},"missingProps":[],"newDescription":"successful operation","oldDescription":"successful operation","schemaChanged":true,"statusCode":"200"}],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":true,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[],"missingResponses":{},"summary":"Find purchase order by ID"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/store/order/{orderId}"},{"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false}],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":false,"diffRequestProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingResponses":{},"summary":"Create user"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user"},{"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false}],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":false,"diffRequestProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingResponses":{},"summary":"Creates list of users with given input array"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/createWithArray"},{"changedOperations":{"POST":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false}],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":false,"diffRequestProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingResponses":{},"summary":"Creates list of users with given input array"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/createWithList"},{"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":false,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[{"description":"The password for login in clear text","extensions":{},"in":"query","name":"password","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[10].changedOperations.GET.missingParameters[0].extensions"},"specVersion":"V30","type":"string","types":["string"]}}],"missingProduces":[],"missingProps":[],"missingRequestProps":[],"missingResponses":{},"summary":"Logs user into the system"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/login"},{"changedOperations":{"GET":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false}],"addRequestProps":[],"addResponses":{},"changedParameter":[{"changeDescription":true,"changeRequired":false,"changed":[{"el":"username","newEnums":false,"property":{"exampleSetFlag":false,"extensions":{},"format":"int32","specVersion":"V30","type":"integer","types":["integer"]},"removedEnums":false,"typeChange":false}],"diff":true,"increased":[],"leftParameter":{"description":"The name that needs to be fetched. Use user1 for testing. ","extensions":{},"in":"path","name":"username","required":true,"schema":{"exampleSetFlag":false,"extensions":{"$ref":"$.changedEndpoints[11].changedOperations.GET.changedParameter[0].leftParameter.extensions"},"specVersion":"V30","type":"string","types":["string"]}},"missing":[],"rightParameter":{"description":"The name that needs to be fetched. Use user1 for testing. edited","extensions":{"$ref":"$.changedEndpoints[11].changedOperations.GET.changedParameter[0].changed[0].property.extensions"},"in":"path","name":"username","required":true,"schema":{"$ref":"$.changedEndpoints[11].changedOperations.GET.changedParameter[0].changed[0].property"}}}],"changedProps":[],"changedRequestProps":[],"changedResponses":[{"addContentTypes":[],"addHeaders":{},"addProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false}],"changedHeaders":[],"changedProps":[],"contentTypeChanged":false,"descriptionChanged":false,"diff":true,"headersChanged":false,"missingContentTypes":[],"missingHeaders":{},"missingProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"newDescription":"successful operation","oldDescription":"successful operation","schemaChanged":true,"statusCode":"200"}],"diff":true,"diffConsumes":false,"diffParam":true,"diffProduces":false,"diffProp":true,"diffRequestProp":false,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingRequestProps":[],"missingResponses":{},"summary":"Get user by user name"},"PUT":{"addConsumes":[],"addParameters":[],"addProduces":[],"addProps":[],"addRequestProps":[{"el":"newUserFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[2].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.newFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.newCatFeild","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.addRequestProps[1].property"},"removedEnums":false,"typeChange":false}],"addResponses":{},"changedParameter":[],"changedProps":[],"changedRequestProps":[],"changedResponses":[],"diff":true,"diffConsumes":false,"diffParam":false,"diffProduces":false,"diffProp":false,"diffRequestProp":true,"missingConsumes":[],"missingParameters":[],"missingProduces":[],"missingProps":[],"missingRequestProps":[{"el":"phone","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[1].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.category.name","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[0].property"},"removedEnums":false,"typeChange":false},{"el":"favorite.tags.removedField","newEnums":false,"property":{"$ref":"$.changedEndpoints[0].changedOperations.PUT.missingRequestProps[2].property"},"removedEnums":false,"typeChange":false}],"missingResponses":{},"summary":"Updated user"}},"diff":true,"missingOperations":{},"newOperations":{},"pathUrl":"/user/{username}"}],"missingEndpoints":[{"method":"POST","operation":{"$ref":"$.changedEndpoints[3].missingOperations.POST"},"pathUrl":"/pet/{petId}","summary":"Updates a pet in the store with form data"}],"newEndpoints":[{"method":"GET","operation":{"$ref":"$.changedEndpoints[3].newOperations.GET"},"pathUrl":"/pet/{petId}","summary":"Find pet by ID"}],"newVersion":"1.0.2","oldVersion":"1.0.0"}
\ No newline at end of file
diff --git a/README.md b/README.md
index 08adffd9..11c4c143 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
  [](https://coveralls.io/github/Sayi/swagger-diff) [](https://maven-badges.herokuapp.com/maven-central/com.deepoove/swagger-diff)
-Compare two swagger API specifications(1.x or v2.0) and render the difference to html file or markdown file.
+Compare two swagger/openAPI API specifications(1.x or v2.0 or openAPI 3.0.x) and render the difference to html file or markdown file.
## :black_large_square: Command line interface (CLI)
@@ -15,7 +15,7 @@ Usage: java -jar swagger-diff.jar [options]
* -new
new api-doc location:Json file path or Http url
-v
- swagger version:1.0 or 2.0
+ swagger version:1.0 or 2.0 or 3.0
Default: 2.0
-output-mode
render mode: markdown or html
@@ -36,7 +36,7 @@ java -jar swagger-diff.jar \
Download the fatJar or view the changelog on the **[Release Page](https://github.com/Sayi/swagger-diff/releases),** and thanks to all contributors.
## Feature
-* Supports swagger spec v1.x and v2.0.
+* Supports swagger spec v1.x v2.0 and openAPI 3.0.x
* Depth comparison of parameters, responses, notes, http method(GET,POST,PUT,DELETE...)
* Supports swagger api Authorization
* Render difference of property with Expression Language
@@ -48,13 +48,13 @@ Download the fatJar or view the changelog on the **[Release Page](https://github
com.deepoove
swagger-diff
- 1.2.2
+ 1.3.0
```
## Gradle
```shell
-compile group: 'com.deepoove', name: 'swagger-diff', version: '1.2.2'
+compile group: 'com.deepoove', name: 'swagger-diff', version: '1.3.0'
```
## Usage
@@ -73,6 +73,11 @@ v2.0
SwaggerDiff.compareV2("petstore_v2_1.json", "petstore_v2_2.json");
```
+openAPI 3.0.x
+```java
+SwaggerDiff.compareV3("petstore_v3_1.json", "petstore_v3_2.json");
+```
+
## Render difference
#### HTML
```java
@@ -157,7 +162,3 @@ try {
## How it works

-
-
-
-
diff --git a/pom.xml b/pom.xml
index 27fc64c4..ebd917dc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
com.deepoove
swagger-diff
- 1.2.2
+ 1.3.0
jar
swagger-diff
@@ -57,14 +57,14 @@
- io.swagger
+ io.swagger.parser.v3
swagger-parser
- 1.0.31
+ 2.1.22
- io.swagger
- swagger-compat-spec-parser
- 1.0.31
+ org.apache.commons
+ commons-lang3
+ 3.12.0
com.j2html
@@ -93,6 +93,11 @@
4.8.2
test
+
+ org.slf4j
+ slf4j-simple
+ 1.7.36
+
@@ -199,6 +204,53 @@
coveralls-maven-plugin
4.3.0
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+
+ com.deepoove.swagger.diff.cli.CLI
+
+
+
+
+
+ maven-assembly-plugin
+
+
+
+ com.deepoove.swagger.diff.cli.CLI
+
+
+
+ jar-with-dependencies
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.12
+
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+
+ report
+ test
+
+ report
+
+
+
+
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 00000000..f8524ee5
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java b/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java
index dfb3ebed..98bf550f 100644
--- a/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java
+++ b/src/main/java/com/deepoove/swagger/diff/SwaggerDiff.java
@@ -1,6 +1,5 @@
package com.deepoove.swagger.diff;
-import java.io.IOException;
import java.util.List;
import org.slf4j.Logger;
@@ -9,129 +8,99 @@
import com.deepoove.swagger.diff.compare.SpecificationDiff;
import com.deepoove.swagger.diff.model.ChangedEndpoint;
import com.deepoove.swagger.diff.model.Endpoint;
-import com.fasterxml.jackson.databind.JsonNode;
-import io.swagger.models.Swagger;
-import io.swagger.models.auth.AuthorizationValue;
-import io.swagger.parser.SwaggerCompatConverter;
-import io.swagger.parser.SwaggerParser;
+import io.swagger.parser.OpenAPIParser;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.parser.core.models.ParseOptions;
+import io.swagger.v3.parser.core.models.SwaggerParseResult;
public class SwaggerDiff {
public static final String SWAGGER_VERSION_V2 = "2.0";
+ public static final String OPENAPI_VERSION_V3 = "3.0";
private static Logger logger = LoggerFactory.getLogger(SwaggerDiff.class);
- private Swagger oldSpecSwagger;
- private Swagger newSpecSwagger;
+ private OpenAPI oldSpecSwagger;
+ private OpenAPI newSpecSwagger;
private List newEndpoints;
private List missingEndpoints;
private List changedEndpoints;
/**
- * compare two swagger 1.x doc
- *
- * @param oldSpec
- * old api-doc location:Json or Http
- * @param newSpec
- * new api-doc location:Json or Http
- */
- public static SwaggerDiff compareV1(String oldSpec, String newSpec) {
- return compare(oldSpec, newSpec, null, null);
- }
-
- /**
- * compare two swagger v2.0 doc
- *
- * @param oldSpec
- * old api-doc location:Json or Http
- * @param newSpec
- * new api-doc location:Json or Http
+ * Compare two OpenAPI/Swagger documents (supports both Swagger 2.0 and OpenAPI 3.x).
+ *
+ * @param oldSpec old api-doc location: file path or HTTP URL
+ * @param newSpec new api-doc location: file path or HTTP URL
*/
public static SwaggerDiff compareV2(String oldSpec, String newSpec) {
- return compare(oldSpec, newSpec, null, SWAGGER_VERSION_V2);
+ return compare(oldSpec, newSpec);
}
-
/**
- * compare two swagger v2.0 Sring
+ * Compare two OpenAPI 3.x documents.
*
- * @param oldSpec old api-doc json as string
- * @param newSpec new api-doc json as string
+ * @param oldSpec old api-doc location: file path or HTTP URL
+ * @param newSpec new api-doc location: file path or HTTP URL
*/
- public static SwaggerDiff compareV2Raw(String oldSpec, String newSpec) {
- return new SwaggerDiff(oldSpec, newSpec).compare();
+ public static SwaggerDiff compareV3(String oldSpec, String newSpec) {
+ return compare(oldSpec, newSpec);
}
-
+
/**
- * Compare two swagger v2.0 docs by JsonNode
- *
- * @param oldSpec
- * old Swagger specification document in v2.0 format as a JsonNode
- * @param newSpec
- * new Swagger specification document in v2.0 format as a JsonNode
+ * Compare two OpenAPI/Swagger documents from raw JSON/YAML strings.
*/
- public static SwaggerDiff compareV2(JsonNode oldSpec, JsonNode newSpec) {
- return new SwaggerDiff(oldSpec, newSpec).compare();
+ public static SwaggerDiff compareRaw(String oldSpec, String newSpec) {
+ return new SwaggerDiff(oldSpec, newSpec, true).compare();
}
- public static SwaggerDiff compare(String oldSpec, String newSpec,
- List auths, String version) {
- return new SwaggerDiff(oldSpec, newSpec, auths, version).compare();
+ /** @deprecated Use compareV2 or compareV3 */
+ @Deprecated
+ public static SwaggerDiff compareV2Raw(String oldSpec, String newSpec) {
+ return compareRaw(oldSpec, newSpec);
}
+ public static SwaggerDiff compare(String oldSpec, String newSpec) {
+ return new SwaggerDiff(oldSpec, newSpec, false).compare();
+ }
- /**
- * @param rawOldSpec
- * @param rawNewSpec
- */
- private SwaggerDiff(String rawOldSpec, String rawNewSpec) {
- SwaggerParser swaggerParser = new SwaggerParser();
- oldSpecSwagger = swaggerParser.parse(rawOldSpec);
- newSpecSwagger = swaggerParser.parse(rawNewSpec);
+ // ---- Constructors ----
- if (null == oldSpecSwagger || null == newSpecSwagger) {
- throw new RuntimeException("cannot read api-doc from spec.");
- }
- }
+ /** Constructor for file path / URL */
+ private SwaggerDiff(String oldSpec, String newSpec, boolean isRawContent) {
+ ParseOptions options = new ParseOptions();
+ options.setResolve(true);
+ options.setResolveFully(true);
- /**
- * @param oldSpec
- * @param newSpec
- * @param auths
- * @param version
- */
- private SwaggerDiff(String oldSpec, String newSpec, List auths,
- String version) {
- if (SWAGGER_VERSION_V2.equals(version)) {
- SwaggerParser swaggerParser = new SwaggerParser();
- oldSpecSwagger = swaggerParser.read(oldSpec, auths, true);
- newSpecSwagger = swaggerParser.read(newSpec, auths, true);
- } else {
- SwaggerCompatConverter swaggerCompatConverter = new SwaggerCompatConverter();
- try {
- oldSpecSwagger = swaggerCompatConverter.read(oldSpec, auths);
- newSpecSwagger = swaggerCompatConverter.read(newSpec, auths);
- } catch (IOException e) {
- logger.error("cannot read api-doc from spec[version_v1.x]", e);
- return;
- }
+ OpenAPIParser parser = new OpenAPIParser();
+
+ SwaggerParseResult oldResult = isRawContent
+ ? parser.readContents(oldSpec, null, options)
+ : parser.readLocation(oldSpec, null, options);
+ SwaggerParseResult newResult = isRawContent
+ ? parser.readContents(newSpec, null, options)
+ : parser.readLocation(newSpec, null, options);
+
+ oldSpecSwagger = oldResult.getOpenAPI();
+ newSpecSwagger = newResult.getOpenAPI();
+
+ if (oldResult.getMessages() != null && !oldResult.getMessages().isEmpty()) {
+ logger.warn("Warnings parsing old spec: {}", oldResult.getMessages());
+ }
+ if (newResult.getMessages() != null && !newResult.getMessages().isEmpty()) {
+ logger.warn("Warnings parsing new spec: {}", newResult.getMessages());
}
- if (null == oldSpecSwagger || null == newSpecSwagger) { throw new RuntimeException(
- "cannot read api-doc from spec."); }
- }
- private SwaggerDiff(JsonNode oldSpec, JsonNode newSpec) {
- SwaggerParser swaggerParser = new SwaggerParser();
- oldSpecSwagger = swaggerParser.read(oldSpec, true);
- newSpecSwagger = swaggerParser.read(newSpec, true);
- if (null == oldSpecSwagger || null == newSpecSwagger) { throw new RuntimeException(
- "cannot read api-doc from spec."); }
+ if (null == oldSpecSwagger || null == newSpecSwagger) {
+ throw new RuntimeException("Cannot read api-doc from spec. " +
+ "Old: " + oldResult.getMessages() +
+ " New: " + newResult.getMessages());
+ }
}
private SwaggerDiff compare() {
- SpecificationDiff diff = SpecificationDiff.diff(oldSpecSwagger, newSpecSwagger);
+ SpecificationDiff diff = SpecificationDiff.diff(oldSpecSwagger, newSpecSwagger);
this.newEndpoints = diff.getNewEndpoints();
this.missingEndpoints = diff.getMissingEndpoints();
this.changedEndpoints = diff.getChangedEndpoints();
@@ -151,10 +120,10 @@ public List getChangedEndpoints() {
}
public String getOldVersion() {
- return oldSpecSwagger.getInfo().getVersion();
+ return oldSpecSwagger.getInfo() != null ? oldSpecSwagger.getInfo().getVersion() : "unknown";
}
public String getNewVersion() {
- return newSpecSwagger.getInfo().getVersion();
+ return newSpecSwagger.getInfo() != null ? newSpecSwagger.getInfo().getVersion() : "unknown";
}
}
diff --git a/src/main/java/com/deepoove/swagger/diff/cli/CLI.java b/src/main/java/com/deepoove/swagger/diff/cli/CLI.java
index 98c195ba..c1857009 100644
--- a/src/main/java/com/deepoove/swagger/diff/cli/CLI.java
+++ b/src/main/java/com/deepoove/swagger/diff/cli/CLI.java
@@ -9,30 +9,33 @@
import com.deepoove.swagger.diff.output.Render;
/**
- * $java -jar swagger-diff.jar -old http://www.petstore.com/swagger.json \n
- * -new http://www.petstore.com/swagger_new.json \n
- * -v 2.0 \n
- * -output-mode markdown \n
+ * $java -jar swagger-diff.jar -old http://www.petstore.com/swagger.json
+ * -new http://www.petstore.com/swagger_new.json
+ * -v 2.0
+ * -output-mode markdown
+ *
+ * Supports both Swagger 2.0 (-v 2.0) and OpenAPI 3.x (-v 3.0).
*
* @author Sayi
- * @version
*/
public class CLI {
private static final String OUTPUT_MODE_MARKDOWN = "markdown";
private static final String OUTPUT_MODE_JSON = "json";
- @Parameter(names = "-old", description = "old api-doc location:Json file path or Http url", required = true, order = 0)
+ @Parameter(names = "-old", description = "old api-doc location: file path or HTTP URL", required = true, order = 0)
private String oldSpec;
- @Parameter(names = "-new", description = "new api-doc location:Json file path or Http url", required = true, order = 1)
+ @Parameter(names = "-new", description = "new api-doc location: file path or HTTP URL", required = true, order = 1)
private String newSpec;
- @Parameter(names = "-v", description = "swagger version:1.0 or 2.0", validateWith= RegexValidator.class, order = 2)
- @Regex("(2\\.0|1\\.0)")
+ @Parameter(names = "-v", description = "swagger/openapi version: 2.0 or 3.0",
+ validateWith = RegexValidator.class, order = 2)
+ @Regex("(2\\.0|1\\.0|3\\.0)")
private String version = SwaggerDiff.SWAGGER_VERSION_V2;
- @Parameter(names = "-output-mode", description = "render mode: markdown, html or json", validateWith = RegexValidator.class, order = 3)
+ @Parameter(names = "-output-mode", description = "render mode: markdown, html or json",
+ validateWith = RegexValidator.class, order = 3)
@Regex("(markdown|html|json)")
private String outputMode = OUTPUT_MODE_MARKDOWN;
@@ -52,47 +55,32 @@ public static void main(String[] args) {
}
public void run(JCommander jCommander) {
- if (help){
+ if (help) {
jCommander.setProgramName("java -jar swagger-diff.jar");
jCommander.usage();
return;
}
- if (v){
- JCommander.getConsole().println("1.2.2");
+ if (v) {
+ JCommander.getConsole().println("1.2.2-oas3");
return;
}
- SwaggerDiff diff = SwaggerDiff.SWAGGER_VERSION_V2.equals(version)
- ? SwaggerDiff.compareV2(oldSpec, newSpec) : SwaggerDiff.compareV1(oldSpec, newSpec);
+ SwaggerDiff diff = SwaggerDiff.OPENAPI_VERSION_V3.equals(version)
+ ? SwaggerDiff.compareV3(oldSpec, newSpec)
+ : SwaggerDiff.compareV2(oldSpec, newSpec);
String render = getRender(outputMode).render(diff);
JCommander.getConsole().println(render);
}
private Render getRender(String outputMode) {
- if (OUTPUT_MODE_MARKDOWN.equals(outputMode)) {
- return new MarkdownRender();
- } else if (OUTPUT_MODE_JSON.equals(outputMode)) {
- return new JsonRender();
- }
+ if (OUTPUT_MODE_MARKDOWN.equals(outputMode)) return new MarkdownRender();
+ if (OUTPUT_MODE_JSON.equals(outputMode)) return new JsonRender();
return new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
}
- public String getOldSpec() {
- return oldSpec;
- }
-
- public String getNewSpec() {
- return newSpec;
- }
-
- public String getVersion() {
- return version;
- }
-
- public String getOutputMode() {
- return outputMode;
- }
-
-
+ public String getOldSpec() { return oldSpec; }
+ public String getNewSpec() { return newSpec; }
+ public String getVersion() { return version; }
+ public String getOutputMode() { return outputMode; }
}
diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java
index 600132f5..543471ad 100644
--- a/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java
+++ b/src/main/java/com/deepoove/swagger/diff/compare/ModelDiff.java
@@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -11,100 +12,148 @@
import com.deepoove.swagger.diff.model.ElProperty;
-import io.swagger.models.ArrayModel;
-import io.swagger.models.Model;
-import io.swagger.models.RefModel;
-import io.swagger.models.properties.ArrayProperty;
-import io.swagger.models.properties.Property;
-import io.swagger.models.properties.RefProperty;
-import io.swagger.models.properties.StringProperty;
+import io.swagger.v3.oas.models.media.Schema;
/**
- * compare two model
- *
- * @author Sayi
- * @version
+ * compare two OAS3 Schema (replaces Model + Property from Swagger 2.0)
+ *
+ * @author Sayi (adapted for OAS3)
*/
+@SuppressWarnings({"rawtypes", "unchecked"})
public class ModelDiff {
private List increased;
private List missing;
private List changed;
- Map oldDedinitions;
- Map newDedinitions;
+ Map oldDefinitions;
+ Map newDefinitions;
private ModelDiff() {
- increased = new ArrayList();
- missing = new ArrayList();
- changed = new ArrayList();
+ increased = new ArrayList<>();
+ missing = new ArrayList<>();
+ changed = new ArrayList<>();
}
- public static ModelDiff buildWithDefinition(Map left, Map right) {
+ public static ModelDiff buildWithDefinition(Map left, Map right) {
ModelDiff diff = new ModelDiff();
- diff.oldDedinitions = left;
- diff.newDedinitions = right;
+ diff.oldDefinitions = left != null ? left : new HashMap<>();
+ diff.newDefinitions = right != null ? right : new HashMap<>();
return diff;
}
- public ModelDiff diff(Model leftModel, Model rightModel) {
- return this.diff(leftModel, rightModel, null, new HashSet());
+ public ModelDiff diff(Schema leftModel, Schema rightModel) {
+ return this.diff(leftModel, rightModel, null, new HashSet<>());
}
- public ModelDiff diff(Model leftModel, Model rightModel, String parentEl) {
- return this.diff(leftModel, rightModel, parentEl, new HashSet());
+ public ModelDiff diff(Schema leftModel, Schema rightModel, String parentEl) {
+ return this.diff(leftModel, rightModel, parentEl, new HashSet<>());
}
- public ModelDiff diff(Property leftProperty, Property rightProperty) {
- return this.diff(findModel(leftProperty, oldDedinitions), findModel(rightProperty, newDedinitions));
+ /** Entry point when comparing two property-level schemas (resolves $ref before diffing) */
+ public ModelDiff diffSchema(Schema leftProperty, Schema rightProperty) {
+ return this.diff(resolveRef(leftProperty, oldDefinitions),
+ resolveRef(rightProperty, newDefinitions));
}
- private ModelDiff diff(Model leftInputModel, Model rightInputModel, String parentEl, Set visited) {
- // Stop recursing if both models are null
- // OR either model is already contained in the visiting history
- if ((null == leftInputModel && null == rightInputModel) || visited.contains(leftInputModel)
+ private ModelDiff diff(Schema leftInputModel, Schema rightInputModel,
+ String parentEl, Set visited) {
+ if ((null == leftInputModel && null == rightInputModel)
+ || visited.contains(leftInputModel)
|| visited.contains(rightInputModel)) {
return this;
}
- Model leftModel = isModelReference(leftInputModel) ? findReferenceModel(leftInputModel, oldDedinitions)
- : leftInputModel;
- Model rightModel = isModelReference(rightInputModel) ? findReferenceModel(rightInputModel, newDedinitions)
- : rightInputModel;
- Map leftProperties = null == leftModel ? null : leftModel.getProperties();
- Map rightProperties = null == rightModel ? null : rightModel.getProperties();
- // Diff the properties
- MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties);
+ Schema leftModel = resolveRef(leftInputModel, oldDefinitions);
+ Schema rightModel = resolveRef(rightInputModel, newDefinitions);
- increased.addAll(convert2ElPropertys(propertyDiff.getIncreased(), parentEl));
- missing.addAll(convert2ElPropertys(propertyDiff.getMissing(), parentEl));
+ while (leftModel != null && leftModel.getItems() != null && rightModel != null && rightModel.getItems() != null) {
+ leftModel = resolveRef(leftModel.getItems(), oldDefinitions);
+ rightModel = resolveRef(rightModel.getItems(), newDefinitions);
+ }
+
+ Map leftProperties = leftModel == null ? null : leftModel.getProperties();
+ Map rightProperties = rightModel == null ? null : rightModel.getProperties();
+
+ MapKeyDiff propertyDiff = MapKeyDiff.diff(leftProperties, rightProperties);
+
+ increased.addAll(convert2ElProperties(propertyDiff.getIncreased(), parentEl));
+ missing.addAll(convert2ElProperties(propertyDiff.getMissing(), parentEl));
+
+ Set newVisited = copyAndAdd(visited, leftModel, rightModel);
- // Recursively find the diff between properties
List sharedKey = propertyDiff.getSharedKey();
- sharedKey.stream().forEach((key) -> {
- Property left = leftProperties.get(key);
- Property right = rightProperties.get(key);
- Model leftSubModel = findModel(left, oldDedinitions);
- Model rightSubModel = findModel(left, newDedinitions);
- if (leftSubModel != null || rightSubModel != null) {
- diff(leftSubModel, rightSubModel, buildElString(parentEl, key),
- copyAndAdd(visited, leftModel, rightModel));
- } else if (left != null && right != null && !left.equals(right)) {
- // Add a changed ElProperty if not a Reference
- // Useless
- changed.add(addChangeMetadata(convert2ElProperty(key, parentEl, left), left, right));
+ sharedKey.forEach(key -> {
+ Schema left = leftProperties.get(key);
+ Schema right = rightProperties.get(key);
+
+ // Try to resolve as sub-model (ref or object with properties)
+ Schema leftSub = resolveToSubModel(left, oldDefinitions);
+ Schema rightSub = resolveToSubModel(right, newDefinitions);
+
+ if (leftSub != null || rightSub != null) {
+ diff(leftSub, rightSub, buildElString(parentEl, key), newVisited);
+ } else if (left != null && right != null) {
+ ElProperty diffProp = convert2ElProperty(key, parentEl, left);
+ addChangeMetadata(diffProp, left, right);
+ if (diffProp.isTypeChange() || diffProp.isNewEnums() || diffProp.isRemovedEnums()) {
+ changed.add(diffProp);
+ }
}
});
+
return this;
}
- private Collection extends ElProperty> convert2ElPropertys(Map propMap, String parentEl) {
+ /** Returns the resolved object schema if schema is a $ref or has properties; null otherwise */
+ private Schema resolveToSubModel(Schema schema, Map definitions) {
+ if (schema == null) return null;
+ if (schema.get$ref() != null) {
+ return definitions.get(getSimpleRef(schema.get$ref()));
+ }
+ // Array: try to resolve items
+ if (schema.getItems() != null) {
+ String itemRef = schema.getItems().get$ref();
+ if (itemRef != null) {
+ return definitions.get(getSimpleRef(itemRef));
+ }
+ if (schema.getItems().getProperties() != null && !schema.getItems().getProperties().isEmpty()) {
+ return schema.getItems();
+ }
+ }
+ // Object inline with properties
+ if (schema.getProperties() != null && !schema.getProperties().isEmpty()) {
+ return schema;
+ }
+ return null;
+ }
+
+ /** Resolve a $ref schema to the referenced schema in definitions */
+ private Schema resolveRef(Schema schema, Map definitions) {
+ if (schema == null) return null;
+ if (schema.get$ref() != null) {
+ String name = getSimpleRef(schema.get$ref());
+ Schema resolved = definitions.get(name);
+ return resolved != null ? resolved : schema;
+ }
+ return schema;
+ }
- List result = new ArrayList();
- if (null == propMap) return result;
+ private boolean schemasCompatible(Schema left, Schema right) {
+ String lt = left.getType();
+ String rt = right.getType();
+ if (lt != null && !lt.equals(rt)) return false;
+ String lr = left.get$ref();
+ String rr = right.get$ref();
+ if (lr != null && !lr.equals(rr)) return false;
+ return true;
+ }
- for (Entry entry : propMap.entrySet()) {
- // TODO Recursively get the properties
+ private Collection extends ElProperty> convert2ElProperties(
+ Map propMap, String parentEl) {
+ List result = new ArrayList<>();
+ if (propMap == null) return result;
+ for (Entry entry : propMap.entrySet()) {
result.add(convert2ElProperty(entry.getKey(), parentEl, entry.getValue()));
}
return result;
@@ -114,21 +163,25 @@ private String buildElString(String parentEl, String propName) {
return null == parentEl ? propName : (parentEl + "." + propName);
}
- private ElProperty convert2ElProperty(String propName, String parentEl, Property property) {
+ private ElProperty convert2ElProperty(String propName, String parentEl, Schema schema) {
ElProperty pWithPath = new ElProperty();
- pWithPath.setProperty(property);
+ pWithPath.setProperty(schema);
pWithPath.setEl(buildElString(parentEl, propName));
return pWithPath;
}
- private ElProperty addChangeMetadata(ElProperty diffProperty, Property left, Property right) {
- diffProperty.setTypeChange(!left.getType().equalsIgnoreCase(right.getType()));
+ private ElProperty addChangeMetadata(ElProperty diffProperty, Schema left, Schema right) {
+ String leftType = left.getType();
+ String rightType = right.getType();
+ diffProperty.setTypeChange(leftType != null && !leftType.equalsIgnoreCase(
+ rightType != null ? rightType : ""));
+
List leftEnums = enumValues(left);
List rightEnums = enumValues(right);
if (!leftEnums.isEmpty() && !rightEnums.isEmpty()) {
ListDiff enumDiff = ListDiff.diff(leftEnums, rightEnums, (t, enumVal) -> {
for (String value : t) {
- if (enumVal.equalsIgnoreCase(value)) { return value; }
+ if (enumVal.equalsIgnoreCase(value)) return value;
}
return null;
});
@@ -140,70 +193,33 @@ private ElProperty addChangeMetadata(ElProperty diffProperty, Property left, Pro
@SuppressWarnings("unchecked")
private Set copyAndAdd(Set set, T... add) {
- Set newSet = new HashSet(set);
+ Set newSet = new HashSet<>(set);
newSet.addAll(Arrays.asList(add));
return newSet;
}
- private List enumValues(Property prop) {
- if (prop instanceof StringProperty && ((StringProperty) prop).getEnum() != null) {
- return ((StringProperty) prop).getEnum();
- } else {
- return new ArrayList<>();
- }
- }
-
- private Model findModel(Property property, Map modelMap) {
- String modelName = null;
- if (property instanceof RefProperty) {
- modelName = ((RefProperty) property).getSimpleRef();
- } else if (property instanceof ArrayProperty) {
- Property arrayType = ((ArrayProperty) property).getItems();
- if (arrayType instanceof RefProperty) {
- modelName = ((RefProperty) arrayType).getSimpleRef();
- }
- }
- return modelName == null ? null : modelMap.get(modelName);
- }
-
- private boolean isModelReference(Model model) {
- return model instanceof RefModel || model instanceof ArrayModel;
- }
-
- private Model findReferenceModel(Model model, Map modelMap) {
- String modelName = null;
- if (model instanceof RefModel) {
- modelName = ((RefModel) model).getSimpleRef();
- } else if (model instanceof ArrayModel) {
- Property arrayType = ((ArrayModel) model).getItems();
- if (arrayType instanceof RefProperty) {
- modelName = ((RefProperty) arrayType).getSimpleRef();
+ private List enumValues(Schema schema) {
+ List result = new ArrayList<>();
+ if (schema.getEnum() != null) {
+ for (Object val : schema.getEnum()) {
+ if (val != null) result.add(val.toString());
}
}
- return modelName == null ? null : modelMap.get(modelName);
- }
-
- public List getIncreased() {
- return increased;
- }
-
- public void setIncreased(List increased) {
- this.increased = increased;
+ return result;
}
- public List getMissing() {
- return missing;
+ private String getSimpleRef(String ref) {
+ if (ref == null) return null;
+ int lastSlash = ref.lastIndexOf('/');
+ return lastSlash >= 0 ? ref.substring(lastSlash + 1) : ref;
}
- public void setMissing(List missing) {
- this.missing = missing;
- }
+ public List getIncreased() { return increased; }
+ public void setIncreased(List increased) { this.increased = increased; }
- public List getChanged() {
- return changed;
- }
+ public List getMissing() { return missing; }
+ public void setMissing(List missing) { this.missing = missing; }
- public void setChanged(List changed) {
- this.changed = changed;
- }
+ public List getChanged() { return changed; }
+ public void setChanged(List changed) { this.changed = changed; }
}
diff --git a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java
index 30e8f3ba..b45a126e 100644
--- a/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java
+++ b/src/main/java/com/deepoove/swagger/diff/compare/ParameterDiff.java
@@ -1,6 +1,7 @@
package com.deepoove.swagger.diff.compare;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -10,38 +11,35 @@
import com.deepoove.swagger.diff.model.ElProperty;
import com.google.common.collect.Lists;
-import io.swagger.models.Model;
-import io.swagger.models.parameters.AbstractSerializableParameter;
-import io.swagger.models.parameters.BodyParameter;
-import io.swagger.models.parameters.Parameter;
-import io.swagger.models.properties.Property;
-import io.swagger.models.properties.StringProperty;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
/**
- * compare two parameter
+ * Compare two lists of OAS3 parameters (path/query/header/cookie only;
+ * request body is handled separately in SpecificationDiff).
*
- * @author Sayi
- * @version
+ * @author Sayi (adapted for OAS3)
*/
+@SuppressWarnings({"rawtypes"})
public class ParameterDiff {
private List increased;
private List missing;
private List changed;
- Map oldDedinitions;
- Map newDedinitions;
+ Map oldDefinitions;
+ Map newDefinitions;
private ParameterDiff() {
- this.increased = new ArrayList();
- this.missing = new ArrayList();
- this.changed = new ArrayList();
+ this.increased = new ArrayList<>();
+ this.missing = new ArrayList<>();
+ this.changed = new ArrayList<>();
}
- public static ParameterDiff buildWithDefinition(Map left, Map right) {
+ public static ParameterDiff buildWithDefinition(Map left, Map right) {
ParameterDiff diff = new ParameterDiff();
- diff.oldDedinitions = left;
- diff.newDedinitions = right;
+ diff.oldDefinitions = left != null ? left : new HashMap<>();
+ diff.newDefinitions = right != null ? right : new HashMap<>();
return diff;
}
@@ -51,97 +49,59 @@ public ParameterDiff diff(List left, List right) {
ListDiff paramDiff = ListDiff.diff(left, right, (t, param) -> {
for (Parameter para : t) {
- if (param.getName().equals(para.getName())) { return para; }
+ if (param.getName() != null && param.getName().equals(para.getName())) return para;
}
return null;
});
this.increased.addAll(paramDiff.getIncreased());
this.missing.addAll(paramDiff.getMissing());
+
Map shared = paramDiff.getShared();
shared.forEach((leftPara, rightPara) -> {
ChangedParameter changedParameter = new ChangedParameter();
changedParameter.setLeftParameter(leftPara);
changedParameter.setRightParameter(rightPara);
- if (leftPara instanceof BodyParameter && rightPara instanceof BodyParameter) {
- BodyParameter leftBodyPara = (BodyParameter) leftPara;
- Model leftSchema = leftBodyPara.getSchema();
- BodyParameter rightBodyPara = (BodyParameter) rightPara;
- Model rightSchema = rightBodyPara.getSchema();
-
- ModelDiff diff = ModelDiff.buildWithDefinition(oldDedinitions, newDedinitions).diff(leftSchema,
- rightSchema, leftPara.getName());
- changedParameter.setIncreased(diff.getIncreased());
- changedParameter.setMissing(diff.getMissing());
- changedParameter.setChanged(diff.getChanged());
-
- }
- // Let's handle the case where the new API has fx changed the type
- // of PathParameter from being of type String to type integer
- if (leftPara instanceof AbstractSerializableParameter
- && rightPara instanceof AbstractSerializableParameter) {
- if (!leftPara.equals(rightPara)) {
+ // Compare schema type changes (replaces AbstractSerializableParameter check)
+ Schema leftSchema = leftPara.getSchema();
+ Schema rightSchema = rightPara.getSchema();
+ if (leftSchema != null && rightSchema != null) {
+ String leftType = leftSchema.getType();
+ String rightType = rightSchema.getType();
+ if (leftType != null && !leftType.equals(rightType)) {
ElProperty elProperty = new ElProperty();
elProperty.setEl(rightPara.getName());
- elProperty.setProperty(mapToProperty(rightPara));
+ elProperty.setProperty(rightSchema);
changedParameter.setChanged(Lists.newArrayList(elProperty));
}
}
- // is requried
- boolean rightRequired = rightPara.getRequired();
- boolean leftRequired = leftPara.getRequired();
+ // required
+ boolean rightRequired = Boolean.TRUE.equals(rightPara.getRequired());
+ boolean leftRequired = Boolean.TRUE.equals(leftPara.getRequired());
changedParameter.setChangeRequired(leftRequired != rightRequired);
// description
String description = rightPara.getDescription();
- String oldPescription = leftPara.getDescription();
+ String oldDescription = leftPara.getDescription();
if (StringUtils.isBlank(description)) description = "";
- if (StringUtils.isBlank(oldPescription)) oldPescription = "";
- changedParameter.setChangeDescription(!description.equals(oldPescription));
+ if (StringUtils.isBlank(oldDescription)) oldDescription = "";
+ changedParameter.setChangeDescription(!description.equals(oldDescription));
if (changedParameter.isDiff()) {
this.changed.add(changedParameter);
}
-
});
return this;
}
- private Property mapToProperty(Parameter rightPara) {
- Property prop = new StringProperty();
- prop.setAccess(rightPara.getAccess());
- prop.setAllowEmptyValue(rightPara.getAllowEmptyValue());
- prop.setDescription(rightPara.getDescription());
- prop.setName(rightPara.getName());
- prop.setReadOnly(rightPara.isReadOnly());
- prop.setRequired(rightPara.getRequired());
- return prop;
- }
-
- public List getIncreased() {
- return increased;
- }
-
- public void setIncreased(List increased) {
- this.increased = increased;
- }
-
- public List getMissing() {
- return missing;
- }
-
- public void setMissing(List missing) {
- this.missing = missing;
- }
-
- public List getChanged() {
- return changed;
- }
+ public List getIncreased() { return increased; }
+ public void setIncreased(List increased) { this.increased = increased; }
- public void setChanged(List changed) {
- this.changed = changed;
- }
+ public List getMissing() { return missing; }
+ public void setMissing(List missing) { this.missing = missing; }
+ public List getChanged() { return changed; }
+ public void setChanged(List changed) { this.changed = changed; }
}
diff --git a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java
index 97817501..ae9df385 100644
--- a/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java
+++ b/src/main/java/com/deepoove/swagger/diff/compare/PropertyDiff.java
@@ -1,65 +1,52 @@
package com.deepoove.swagger.diff.compare;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.deepoove.swagger.diff.model.ElProperty;
-import io.swagger.models.Model;
-import io.swagger.models.properties.Property;
+import io.swagger.v3.oas.models.media.Schema;
+@SuppressWarnings({"rawtypes"})
public class PropertyDiff {
private List increased;
private List missing;
private List changed;
- Map oldDedinitions;
- Map newDedinitions;
+ Map oldDefinitions;
+ Map newDefinitions;
private PropertyDiff() {
- increased = new ArrayList();
- missing = new ArrayList();
- changed = new ArrayList();
+ increased = new ArrayList<>();
+ missing = new ArrayList<>();
+ changed = new ArrayList<>();
}
- public static PropertyDiff buildWithDefinition(Map left, Map right) {
+ public static PropertyDiff buildWithDefinition(Map left, Map right) {
PropertyDiff diff = new PropertyDiff();
- diff.oldDedinitions = left;
- diff.newDedinitions = right;
+ diff.oldDefinitions = left != null ? left : new HashMap<>();
+ diff.newDefinitions = right != null ? right : new HashMap<>();
return diff;
}
- public PropertyDiff diff(Property left, Property right) {
- ModelDiff diff = ModelDiff.buildWithDefinition(oldDedinitions, newDedinitions).diff(left, right);
- increased.addAll(diff.getIncreased());
- missing.addAll(diff.getMissing());
- changed.addAll(diff.getChanged());
+ public PropertyDiff diff(Schema left, Schema right) {
+ ModelDiff modelDiff = ModelDiff.buildWithDefinition(oldDefinitions, newDefinitions)
+ .diffSchema(left, right);
+ increased.addAll(modelDiff.getIncreased());
+ missing.addAll(modelDiff.getMissing());
+ changed.addAll(modelDiff.getChanged());
return this;
}
- public List getIncreased() {
- return increased;
- }
+ public List getIncreased() { return increased; }
+ public void setIncreased(List increased) { this.increased = increased; }
- public void setIncreased(List increased) {
- this.increased = increased;
- }
+ public List getMissing() { return missing; }
+ public void setMissing(List missing) { this.missing = missing; }
- public List getMissing() {
- return missing;
- }
-
- public void setMissing(List missing) {
- this.missing = missing;
- }
-
- public List getChanged() {
- return changed;
- }
-
- public void setChanged(List changed) {
- this.changed = changed;
- }
+ public List getChanged() { return changed; }
+ public void setChanged(List changed) { this.changed = changed; }
}
diff --git a/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java b/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java
index 2e0e6d66..0bc1441a 100644
--- a/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java
+++ b/src/main/java/com/deepoove/swagger/diff/compare/SpecificationDiff.java
@@ -8,22 +8,28 @@
import com.deepoove.swagger.diff.model.ChangedEndpoint;
import com.deepoove.swagger.diff.model.ChangedOperation;
+import com.deepoove.swagger.diff.model.ChangedResponse;
import com.deepoove.swagger.diff.model.Endpoint;
-import io.swagger.models.HttpMethod;
-import io.swagger.models.Operation;
-import io.swagger.models.Path;
-import io.swagger.models.Response;
-import io.swagger.models.Swagger;
-import io.swagger.models.parameters.Parameter;
-import io.swagger.models.properties.Property;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.media.Content;
+import io.swagger.v3.oas.models.media.MediaType;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import io.swagger.v3.oas.models.parameters.RequestBody;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.responses.ApiResponses;
+import io.swagger.v3.oas.models.headers.Header;
+
/**
- * compare two Swagger
- *
- * @author Sayi
+ * Compare two OpenAPI 3.x specifications.
*
+ * @author Sayi (adapted for OAS3)
*/
+@SuppressWarnings({"rawtypes"})
public class SpecificationDiff {
private List newEndpoints;
@@ -32,70 +38,152 @@ public class SpecificationDiff {
private SpecificationDiff() {}
- public static SpecificationDiff diff(Swagger oldSpec, Swagger newSpec) {
- if (null == oldSpec || null == newSpec) { throw new IllegalArgumentException("cannot diff null spec."); }
+ public static SpecificationDiff diff(OpenAPI oldSpec, OpenAPI newSpec) {
+ if (null == oldSpec || null == newSpec) {
+ throw new IllegalArgumentException("cannot diff null spec.");
+ }
SpecificationDiff instance = new SpecificationDiff();
- Map oldPaths = oldSpec.getPaths();
- Map newPaths = newSpec.getPaths();
- // Diff path
- MapKeyDiff pathDiff = MapKeyDiff.diff(oldPaths, newPaths);
+ Map oldDefs = getSchemas(oldSpec);
+ Map newDefs = getSchemas(newSpec);
+
+ Map oldPaths = oldSpec.getPaths() != null ? oldSpec.getPaths() : new HashMap<>();
+ Map newPaths = newSpec.getPaths() != null ? newSpec.getPaths() : new HashMap<>();
+
+ // Diff paths
+ MapKeyDiff pathDiff = MapKeyDiff.diff(oldPaths, newPaths);
instance.newEndpoints = convert2EndpointList(pathDiff.getIncreased());
instance.missingEndpoints = convert2EndpointList(pathDiff.getMissing());
instance.changedEndpoints = new ArrayList<>();
List sharedKey = pathDiff.getSharedKey();
- sharedKey.stream().forEach((pathUrl) -> {
+ sharedKey.forEach(pathUrl -> {
ChangedEndpoint changedEndpoint = new ChangedEndpoint();
changedEndpoint.setPathUrl(pathUrl);
- Path oldPath = oldPaths.get(pathUrl);
- Path newPath = newPaths.get(pathUrl);
-
- // Diff Operation
- Map oldOperationMap = oldPath.getOperationMap();
- Map newOperationMap = newPath.getOperationMap();
- MapKeyDiff operationDiff = MapKeyDiff.diff(oldOperationMap, newOperationMap);
- Map increasedOperation = operationDiff.getIncreased();
- Map missingOperation = operationDiff.getMissing();
- changedEndpoint.setNewOperations(increasedOperation);
- changedEndpoint.setMissingOperations(missingOperation);
-
- List sharedMethods = operationDiff.getSharedKey();
- Map operas = new HashMap<>();
- sharedMethods.stream().forEach((method) -> {
+ PathItem oldPath = oldPaths.get(pathUrl);
+ PathItem newPath = newPaths.get(pathUrl);
+
+ // Diff operations
+ Map oldOpMap = oldPath.readOperationsMap() != null
+ ? oldPath.readOperationsMap() : new HashMap<>();
+ Map newOpMap = newPath.readOperationsMap() != null
+ ? newPath.readOperationsMap() : new HashMap<>();
+
+ MapKeyDiff operationDiff =
+ MapKeyDiff.diff(oldOpMap, newOpMap);
+
+ changedEndpoint.setNewOperations(operationDiff.getIncreased());
+ changedEndpoint.setMissingOperations(operationDiff.getMissing());
+
+ Map operas = new HashMap<>();
+ operationDiff.getSharedKey().forEach(method -> {
ChangedOperation changedOperation = new ChangedOperation();
- Operation oldOperation = oldOperationMap.get(method);
- Operation newOperation = newOperationMap.get(method);
- changedOperation.setSummary(newOperation.getSummary());
-
- // Diff Parameter
- List oldParameters = oldOperation.getParameters();
- List newParameters = newOperation.getParameters();
- ParameterDiff parameterDiff = ParameterDiff
- .buildWithDefinition(oldSpec.getDefinitions(), newSpec.getDefinitions())
- .diff(oldParameters, newParameters);
- changedOperation.setAddParameters(parameterDiff.getIncreased());
- changedOperation.setMissingParameters(parameterDiff.getMissing());
- changedOperation.setChangedParameter(parameterDiff.getChanged());
-
- // Diff response
- Property oldResponseProperty = getResponseProperty(oldOperation);
- Property newResponseProperty = getResponseProperty(newOperation);
- PropertyDiff propertyDiff = PropertyDiff.buildWithDefinition(oldSpec.getDefinitions(),
- newSpec.getDefinitions());
- propertyDiff.diff(oldResponseProperty, newResponseProperty);
+ Operation oldOp = oldOpMap.get(method);
+ Operation newOp = newOpMap.get(method);
+ changedOperation.setSummary(newOp.getSummary());
+
+ // Diff parameters (path/query/header/cookie — NOT body)
+ List oldParams = oldOp.getParameters() != null
+ ? oldOp.getParameters() : new ArrayList<>();
+ List newParams = newOp.getParameters() != null
+ ? newOp.getParameters() : new ArrayList<>();
+ ParameterDiff paramDiff = ParameterDiff.buildWithDefinition(oldDefs, newDefs)
+ .diff(oldParams, newParams);
+ changedOperation.setAddParameters(paramDiff.getIncreased());
+ changedOperation.setMissingParameters(paramDiff.getMissing());
+ changedOperation.setChangedParameter(paramDiff.getChanged());
+
+ // Diff request body schema
+ Schema oldRequestSchema = getRequestSchema(oldOp);
+ Schema newRequestSchema = getRequestSchema(newOp);
+ PropertyDiff requestPropertyDiff = PropertyDiff.buildWithDefinition(oldDefs, newDefs);
+ requestPropertyDiff.diff(oldRequestSchema, newRequestSchema);
+ changedOperation.setAddRequestProps(requestPropertyDiff.getIncreased());
+ changedOperation.setMissingRequestProps(requestPropertyDiff.getMissing());
+ changedOperation.setChangedRequestProps(requestPropertyDiff.getChanged());
+
+ // --- LEGACY: diff schema del 200 sui campi addProps/missingProps/changedProps
+ // (mantenuto per retrocompatibilità con i test/render esistenti) ---
+ Schema oldResponseSchema = getResponseSchema(oldOp); // il vecchio helper che prende il 200
+ Schema newResponseSchema = getResponseSchema(newOp);
+ PropertyDiff propertyDiff = PropertyDiff.buildWithDefinition(oldDefs, newDefs);
+ propertyDiff.diff(oldResponseSchema, newResponseSchema);
changedOperation.setAddProps(propertyDiff.getIncreased());
changedOperation.setMissingProps(propertyDiff.getMissing());
changedOperation.setChangedProps(propertyDiff.getChanged());
+ // Diff RESPONSES per status code
+ ApiResponses oldResponses = oldOp.getResponses() != null
+ ? oldOp.getResponses() : new ApiResponses();
+ ApiResponses newResponses = newOp.getResponses() != null
+ ? newOp.getResponses() : new ApiResponses();
+
+ MapKeyDiff responseDiff = MapKeyDiff.diff(oldResponses, newResponses);
+
+ // status aggiunti (es. nuovo 201) e rimossi (es. 404 tolto)
+ changedOperation.setAddResponses(responseDiff.getIncreased());
+ changedOperation.setMissingResponses(responseDiff.getMissing());
+
+ // status presenti in entrambi: diff dello schema per ciascuno
+ List changedResponseList = new ArrayList<>();
+
+ responseDiff.getSharedKey().forEach(statusCode -> {
+ ApiResponse oldResp = oldResponses.get(statusCode);
+ ApiResponse newResp = newResponses.get(statusCode);
+
+ ChangedResponse cr = new ChangedResponse();
+ cr.setStatusCode(statusCode);
+
+ // --- description ---
+ cr.setOldDescription(oldResp != null ? oldResp.getDescription() : null);
+ cr.setNewDescription(newResp != null ? newResp.getDescription() : null);
+
+ // --- schema (application/json, come prima) ---
+ Schema oldRespSchema = getResponseSchema(oldResp);
+ Schema newRespSchema = getResponseSchema(newResp);
+ PropertyDiff respPropDiff = PropertyDiff.buildWithDefinition(oldDefs, newDefs);
+ respPropDiff.diff(oldRespSchema, newRespSchema);
+ cr.setAddProps(respPropDiff.getIncreased());
+ cr.setMissingProps(respPropDiff.getMissing());
+ cr.setChangedProps(respPropDiff.getChanged());
+
+ // --- content-type ---
+ ListDiff ctDiff = getMediaTypeDiff(
+ getContentTypes(oldResp), getContentTypes(newResp));
+ cr.setAddContentTypes(ctDiff.getIncreased());
+ cr.setMissingContentTypes(ctDiff.getMissing());
+
+ // --- headers ---
+ Map oldHeaders = getHeaders(oldResp);
+ Map newHeaders = getHeaders(newResp);
+ MapKeyDiff headerDiff = MapKeyDiff.diff(oldHeaders, newHeaders);
+ cr.setAddHeaders(headerDiff.getIncreased());
+ cr.setMissingHeaders(headerDiff.getMissing());
+ // header presenti in entrambi ma cambiati (per tipo/descrizione)
+ List changedHeaderNames = new ArrayList<>();
+ headerDiff.getSharedKey().forEach(headerName -> {
+ Header oh = oldHeaders.get(headerName);
+ Header nh = newHeaders.get(headerName);
+ if (isHeaderChanged(oh, nh)) {
+ changedHeaderNames.add(headerName);
+ }
+ });
+ cr.setChangedHeaders(changedHeaderNames);
- // Diff Consumes
- ListDiff consumeDiff = getMediaTypeDiff(oldOperation.getConsumes(), newOperation.getConsumes());
+ if (cr.isDiff()) {
+ changedResponseList.add(cr);
+ }
+ });
+ changedOperation.setChangedResponses(changedResponseList);
+
+ // Diff consumes (request body content types)
+ ListDiff consumeDiff = getMediaTypeDiff(
+ getRequestContentTypes(oldOp), getRequestContentTypes(newOp));
changedOperation.setAddConsumes(consumeDiff.getIncreased());
changedOperation.setMissingConsumes(consumeDiff.getMissing());
- // Diff Produces
- ListDiff producesDiff = getMediaTypeDiff(oldOperation.getProduces(),
- newOperation.getProduces());
+ // Diff produces (response content types)
+ ListDiff producesDiff = getMediaTypeDiff(
+ getResponseContentTypes(oldOp), getResponseContentTypes(newOp));
changedOperation.setAddProduces(producesDiff.getIncreased());
changedOperation.setMissingProduces(producesDiff.getMissing());
@@ -103,12 +191,13 @@ public static SpecificationDiff diff(Swagger oldSpec, Swagger newSpec) {
operas.put(method, changedOperation);
}
});
+
changedEndpoint.setChangedOperations(operas);
- instance.newEndpoints
- .addAll(convert2EndpointList(changedEndpoint.getPathUrl(), changedEndpoint.getNewOperations()));
- instance.missingEndpoints
- .addAll(convert2EndpointList(changedEndpoint.getPathUrl(), changedEndpoint.getMissingOperations()));
+ instance.newEndpoints.addAll(
+ convert2EndpointList(changedEndpoint.getPathUrl(), changedEndpoint.getNewOperations()));
+ instance.missingEndpoints.addAll(
+ convert2EndpointList(changedEndpoint.getPathUrl(), changedEndpoint.getMissingOperations()));
if (changedEndpoint.isDiff()) {
instance.changedEndpoints.add(changedEndpoint);
@@ -116,28 +205,86 @@ public static SpecificationDiff diff(Swagger oldSpec, Swagger newSpec) {
});
return instance;
+ }
+ // ---- Helpers ----
+
+ @SuppressWarnings("unchecked")
+ private static Map getSchemas(OpenAPI api) {
+ if (api.getComponents() == null) return new HashMap<>();
+ Map schemas = api.getComponents().getSchemas();
+ return schemas != null ? schemas : new HashMap<>();
}
- private static Property getResponseProperty(Operation operation) {
- Map responses = operation.getResponses();
- // temporary workaround for missing response messages
+ // TODO: remove this old method that only manage 200 response
+ private static Schema getResponseSchema(Operation operation) {
+ ApiResponses responses = operation.getResponses();
if (responses == null) return null;
- Response response = responses.get("200");
- return null == response ? null : response.getSchema();
+ ApiResponse response = responses.get("200");
+ if (response == null && !responses.isEmpty()) {
+ response = responses.values().iterator().next();
+ }
+ if (response == null) return null;
+ Content content = response.getContent();
+ if (content == null || content.isEmpty()) return null;
+ MediaType mediaType = content.get("application/json");
+ if (mediaType == null) mediaType = content.values().iterator().next();
+ return mediaType != null ? mediaType.getSchema() : null;
+ }
+
+private static Schema getResponseSchema(ApiResponse response) {
+ if (response == null) { System.out.println(">>> response NULL"); return null; }
+ Content content = response.getContent();
+ //System.out.println(">>> response content keys = " + (content != null ? content.keySet() : "NULL"));
+ if (content == null || content.isEmpty()) return null;
+ MediaType mediaType = content.get("application/json");
+ if (mediaType == null) mediaType = content.values().iterator().next();
+ Schema s = mediaType != null ? mediaType.getSchema() : null;
+ //System.out.println(">>> response schema = " + (s != null ? s.getType() + " ref=" + s.get$ref() : "NULL"));
+ return s;
+}
+
+ private static List getResponseContentTypes(Operation operation) {
+ ApiResponses responses = operation.getResponses();
+ if (responses == null) return new ArrayList<>();
+ ApiResponse response = responses.get("200");
+ if (response == null && !responses.isEmpty()) {
+ response = responses.values().iterator().next();
+ }
+ if (response == null) return new ArrayList<>();
+ Content content = response.getContent();
+ return content != null ? new ArrayList<>(content.keySet()) : new ArrayList<>();
+ }
+
+ private static List getRequestContentTypes(Operation operation) {
+ RequestBody requestBody = operation.getRequestBody();
+ if (requestBody == null) return new ArrayList<>();
+ Content content = requestBody.getContent();
+ return content != null ? new ArrayList<>(content.keySet()) : new ArrayList<>();
+ }
+
+ private static Schema getRequestSchema(Operation operation) {
+ RequestBody requestBody = operation.getRequestBody();
+ if (requestBody == null) return null;
+ Content content = requestBody.getContent();
+ if (content == null || content.isEmpty()) return null;
+ MediaType mediaType = content.get("application/json");
+ if (mediaType == null) mediaType = content.values().iterator().next();
+ return mediaType != null ? mediaType.getSchema() : null;
}
- private static List convert2EndpointList(Map map) {
- List endpoints = new ArrayList();
+ private static List convert2EndpointList(Map map) {
+ List endpoints = new ArrayList<>();
if (null == map) return endpoints;
- map.forEach((url, path) -> {
- Map operationMap = path.getOperationMap();
- operationMap.forEach((httpMethod, operation) -> {
+ map.forEach((url, pathItem) -> {
+ Map opMap = pathItem.readOperationsMap();
+ if (opMap == null) return;
+ opMap.forEach((httpMethod, operation) -> {
Endpoint endpoint = new Endpoint();
endpoint.setPathUrl(url);
endpoint.setMethod(httpMethod);
endpoint.setSummary(operation.getSummary());
- endpoint.setPath(path);
+ endpoint.setPath(pathItem);
endpoint.setOperation(operation);
endpoints.add(endpoint);
});
@@ -145,8 +292,9 @@ private static List convert2EndpointList(Map map) {
return endpoints;
}
- private static Collection extends Endpoint> convert2EndpointList(String pathUrl, Map map) {
- List endpoints = new ArrayList();
+ private static Collection extends Endpoint> convert2EndpointList(
+ String pathUrl, Map map) {
+ List endpoints = new ArrayList<>();
if (null == map) return endpoints;
map.forEach((httpMethod, operation) -> {
Endpoint endpoint = new Endpoint();
@@ -162,22 +310,42 @@ private static Collection extends Endpoint> convert2EndpointList(String pathUr
private static ListDiff getMediaTypeDiff(List oldTypes, List newTypes) {
return ListDiff.diff(oldTypes, newTypes, (t, sample) -> {
for (String mediaType : t) {
- if (sample.equalsIgnoreCase(mediaType)) { return mediaType; }
+ if (sample.equalsIgnoreCase(mediaType)) return mediaType;
}
return null;
});
}
- public List getNewEndpoints() {
- return newEndpoints;
- }
+ public List getNewEndpoints() { return newEndpoints; }
+ public List getMissingEndpoints() { return missingEndpoints; }
+ public List getChangedEndpoints() { return changedEndpoints; }
+
- public List getMissingEndpoints() {
- return missingEndpoints;
+ private static List getContentTypes(ApiResponse response) {
+ if (response == null) return new ArrayList<>();
+ Content content = response.getContent();
+ return content != null ? new ArrayList<>(content.keySet()) : new ArrayList<>();
}
- public List getChangedEndpoints() {
- return changedEndpoints;
+ private static Map getHeaders(ApiResponse response) {
+ if (response == null || response.getHeaders() == null) return new HashMap<>();
+ return response.getHeaders();
}
+ /**
+ * Confronto "leggero" di due header: cambio di descrizione, deprecated o tipo schema.
+ */
+ private static boolean isHeaderChanged(Header oldHeader, Header newHeader) {
+ if (oldHeader == null || newHeader == null) return oldHeader != newHeader;
+
+ if (!java.util.Objects.equals(oldHeader.getDescription(), newHeader.getDescription())) return true;
+ if (!java.util.Objects.equals(oldHeader.getDeprecated(), newHeader.getDeprecated())) return true;
+ if (!java.util.Objects.equals(oldHeader.getRequired(), newHeader.getRequired())) return true;
+
+ String oldType = oldHeader.getSchema() != null ? oldHeader.getSchema().getType() : null;
+ String newType = newHeader.getSchema() != null ? newHeader.getSchema().getType() : null;
+ if (!java.util.Objects.equals(oldType, newType)) return true;
+
+ return false;
+ }
}
diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java
index 270972b2..77f07cfb 100644
--- a/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java
+++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedEndpoint.java
@@ -2,39 +2,39 @@
import java.util.Map;
-import io.swagger.models.HttpMethod;
-import io.swagger.models.Operation;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
public class ChangedEndpoint implements Changed {
private String pathUrl;
- private Map newOperations;
- private Map missingOperations;
+ private Map newOperations;
+ private Map missingOperations;
- private Map changedOperations;
+ private Map changedOperations;
- public Map getNewOperations() {
+ public Map getNewOperations() {
return newOperations;
}
- public void setNewOperations(Map newOperations) {
+ public void setNewOperations(Map newOperations) {
this.newOperations = newOperations;
}
- public Map getMissingOperations() {
+ public Map getMissingOperations() {
return missingOperations;
}
- public void setMissingOperations(Map missingOperations) {
+ public void setMissingOperations(Map missingOperations) {
this.missingOperations = missingOperations;
}
- public Map getChangedOperations() {
+ public Map getChangedOperations() {
return changedOperations;
}
- public void setChangedOperations(Map changedOperations) {
+ public void setChangedOperations(Map changedOperations) {
this.changedOperations = changedOperations;
}
@@ -47,9 +47,6 @@ public void setPathUrl(String pathUrl) {
}
public boolean isDiff() {
- // newOperations.isEmpty()
- // || !missingOperations.isEmpty()
- // ||
return !changedOperations.isEmpty();
}
diff --git a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java
index 8703cf5c..6e5a2175 100644
--- a/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java
+++ b/src/main/java/com/deepoove/swagger/diff/model/ChangedOperation.java
@@ -1,40 +1,50 @@
package com.deepoove.swagger.diff.model;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
-import io.swagger.models.parameters.Parameter;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.Operation;
+
+import io.swagger.v3.oas.models.responses.ApiResponse;
public class ChangedOperation implements Changed {
private String summary;
- private List addParameters = new ArrayList();
- private List missingParameters = new ArrayList();
+ private List addParameters = new ArrayList<>();
+ private List missingParameters = new ArrayList<>();
+
+ private List changedParameter = new ArrayList<>();
- private List changedParameter = new ArrayList();
+ private List addProps = new ArrayList<>();
+ private List missingProps = new ArrayList<>();
+ private List changedProps = new ArrayList<>();
+
+ private List addRequestProps = new ArrayList<>();
+ private List missingRequestProps = new ArrayList<>();
+ private List changedRequestProps = new ArrayList<>();
- private List addProps = new ArrayList();
- private List missingProps = new ArrayList();
- private List changedProps = new ArrayList();
private List addConsumes = new ArrayList<>();
private List missingConsumes = new ArrayList<>();
private List addProduces = new ArrayList<>();
private List missingProduces = new ArrayList<>();
- public List getAddParameters() {
+ public List getAddParameters() {
return addParameters;
}
- public void setAddParameters(List addParameters) {
+ public void setAddParameters(List addParameters) {
this.addParameters = addParameters;
}
- public List getMissingParameters() {
+ public List getMissingParameters() {
return missingParameters;
}
- public void setMissingParameters(List missingParameters) {
+ public void setMissingParameters(List missingParameters) {
this.missingParameters = missingParameters;
}
@@ -79,14 +89,20 @@ public void setSummary(String summary) {
}
public boolean isDiff() {
- return !addParameters.isEmpty() || !missingParameters.isEmpty() || !changedParameter.isEmpty() || isDiffProp()
- || isDiffConsumes() || isDiffProduces();
+ return !addParameters.isEmpty() || !missingParameters.isEmpty() || !changedParameter.isEmpty()
+ || isDiffProp() || isDiffRequestProp() || isDiffConsumes() || isDiffProduces()|| !addResponses.isEmpty()
+ || !missingResponses.isEmpty() || !changedResponses.isEmpty();
}
+
public boolean isDiffProp() {
return !addProps.isEmpty() || !missingProps.isEmpty() || !changedProps.isEmpty();
}
+ public boolean isDiffRequestProp() {
+ return !addRequestProps.isEmpty() || !missingRequestProps.isEmpty() || !changedRequestProps.isEmpty();
+ }
+
public boolean isDiffParam() {
return !addParameters.isEmpty() || !missingParameters.isEmpty() || !changedParameter.isEmpty();
}
@@ -130,4 +146,49 @@ public List getMissingProduces() {
public void setMissingProduces(List missing) {
this.missingProduces = missing == null ? new ArrayList<>() : missing;
}
-}
+
+ public List getAddRequestProps() {
+ return addRequestProps;
+ }
+
+ public void setAddRequestProps(List addRequestProps) {
+ this.addRequestProps = addRequestProps;
+ }
+
+ public List getMissingRequestProps() {
+ return missingRequestProps;
+ }
+
+ public void setMissingRequestProps(List missingRequestProps) {
+ this.missingRequestProps = missingRequestProps;
+ }
+
+ public List getChangedRequestProps() {
+ return changedRequestProps;
+ }
+
+ public void setChangedRequestProps(List changedRequestProps) {
+ this.changedRequestProps = changedRequestProps;
+ }
+
+ // --- Response diffs per status code ---
+ private Map addResponses = new LinkedHashMap<>();
+ private Map missingResponses = new LinkedHashMap<>();
+ // status presenti in entrambi ma con schema cambiato
+ private List