Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException when comparing composed schemas #317

Closed
julienrf opened this issue Jan 9, 2022 · 0 comments · Fixed by #318
Closed

NullPointerException when comparing composed schemas #317

julienrf opened this issue Jan 9, 2022 · 0 comments · Fixed by #318
Labels
Milestone

Comments

@julienrf
Copy link
Contributor

julienrf commented Jan 9, 2022

I get a NullPointerException when I try to compare two schemas defined as a oneOf, if one of them is referred to via a allOf and the other one is referred to via a $ref. Let me show you a test case:

Old OpenAPI:

{ "openapi":"3.0.0", "info":{ "title":"API", "version":"0.1.0" }, "paths":{ "/resource":{ "post":{ "responses":{ "200":{ "description":"Created resource", "content":{ "application/json":{ "schema":{ "type":"string" } } } } }, "summary":"Create resource", "requestBody":{ "content":{ "application/json":{ "schema":{ "$ref":"#/components/schemas/Resource" } } }, "description":"Definition of the resource" } } } }, "components":{ "schemas":{ "Resource":{ "type":"object", "properties":{ "assignment":{ "$ref":"#/components/schemas/Foo" } } }, "Foo":{ "oneOf":[ { "$ref":"#/components/schemas/Foo.Bar" }, { "$ref":"#/components/schemas/Foo.Baz" } ], "discriminator":{ "propertyName":"type", "mapping":{ "Bar":"#/components/schemas/Foo.Bar", "Baz":"#/components/schemas/Foo.Baz" } } }, "Foo.Bar":{ "type":"object", "properties":{ "type":{ "type":"string" } } }, "Foo.Baz":{ "type":"object", "properties":{ "type":{ "type":"string" } } } }, "securitySchemes":{
}

}
}

New OpenAPI:

{ "openapi":"3.0.0", "info":{ "title":"API", "version":"0.1.0" }, "paths":{ "/resource":{ "post":{ "responses":{ "200":{ "description":"Created resource", "content":{ "application/json":{ "schema":{ "type":"string" } } } } }, "summary":"Create resource", "requestBody":{ "content":{ "application/json":{ "schema":{ "$ref":"#/components/schemas/Resource" } } }, "description":"Definition of the resource" } } } }, "components":{ "schemas":{ "Resource":{ "type":"object", "properties":{ "assignment":{ "default":{ "type":"Bar" }, "allOf":[ { "$ref":"#/components/schemas/Foo" } ] } } }, "Foo":{ "oneOf":[ { "$ref":"#/components/schemas/Foo.Bar" }, { "$ref":"#/components/schemas/Foo.Baz" } ], "discriminator":{ "propertyName":"type", "mapping":{ "Bar":"#/components/schemas/Foo.Bar", "Baz":"#/components/schemas/Foo.Baz" } } }, "Foo.Bar":{ "type":"object", "properties":{ "type":{ "type":"string" } } }, "Foo.Baz":{ "type":"object", "properties":{ "type":{ "type":"string" } } } }, "securitySchemes":{
}

}
}

The difference between both is just that:

--- old-open-api.json	2022-01-09 23:22:47.307460584 +0100
+++ new-open-api.json	2022-01-09 23:24:02.127524148 +0100
@@ -39,7 +39,14 @@
         "type":"object",
         "properties":{
           "assignment":{
-            "$ref":"#/components/schemas/Foo"
+            "default":{
+              "type":"Bar"
+            },
+            "allOf":[
+              {
+                "$ref":"#/components/schemas/Foo"
+              }
+            ]
           }
         }
       },

So, in the old version we refer to the schema Foo via a direct "$ref": "#/components/schemas/Foo", whereas in the new version we use an "allOf": [{ "$ref": "#/components/schemas/Foo" }]. The allOf trick is useful because it allows us to add additional information about the referenced schemas (see the default value in my example).

If I try to diff both documents, I get the following stack trace:

$ docker run --rm -t \
  -v $(pwd):/specs:ro \
  openapitools/openapi-diff:latest /specs/old-open-api.json /specs/new-open-api.json
Unexpected exception. Reason: null
java.lang.NullPointerException
	at org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult.getMapping(ComposedSchemaDiffResult.java:100)
	at org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult.diff(ComposedSchemaDiffResult.java:62)
	at org.openapitools.openapidiff.core.compare.SchemaDiff.computeDiff(SchemaDiff.java:316)
	at org.openapitools.openapidiff.core.compare.SchemaDiff.computeDiff(SchemaDiff.java:27)
	at org.openapitools.openapidiff.core.compare.ReferenceDiffCache.cachedDiff(ReferenceDiffCache.java:51)
	at org.openapitools.openapidiff.core.compare.SchemaDiff.diff(SchemaDiff.java:282)
	at org.openapitools.openapidiff.core.compare.schemadiffresult.SchemaDiffResult.diff(SchemaDiffResult.java:70)
	at org.openapitools.openapidiff.core.compare.SchemaDiff.computeDiff(SchemaDiff.java:316)
	at org.openapitools.openapidiff.core.compare.SchemaDiff.computeDiff(SchemaDiff.java:27)
	at org.openapitools.openapidiff.core.compare.ReferenceDiffCache.cachedDiff(ReferenceDiffCache.java:44)
	at org.openapitools.openapidiff.core.compare.SchemaDiff.diff(SchemaDiff.java:282)
	at org.openapitools.openapidiff.core.compare.ContentDiff.diff(ContentDiff.java:33)
	at org.openapitools.openapidiff.core.compare.RequestBodyDiff.computeDiff(RequestBodyDiff.java:77)
	at org.openapitools.openapidiff.core.compare.RequestBodyDiff.computeDiff(RequestBodyDiff.java:17)
	at org.openapitools.openapidiff.core.compare.ReferenceDiffCache.cachedDiff(ReferenceDiffCache.java:51)
	at org.openapitools.openapidiff.core.compare.RequestBodyDiff.diff(RequestBodyDiff.java:34)
	at org.openapitools.openapidiff.core.compare.OperationDiff.diff(OperationDiff.java:42)
	at org.openapitools.openapidiff.core.compare.PathDiff.diff(PathDiff.java:35)
	at org.openapitools.openapidiff.core.compare.PathsDiff.lambda$diff$2(PathsDiff.java:67)
	at java.util.LinkedHashMap$LinkedKeySet.forEach(LinkedHashMap.java:559)
	at org.openapitools.openapidiff.core.compare.PathsDiff.diff(PathsDiff.java:40)
	at org.openapitools.openapidiff.core.compare.OpenApiDiff.compare(OpenApiDiff.java:95)
	at org.openapitools.openapidiff.core.compare.OpenApiDiff.compare(OpenApiDiff.java:66)
	at org.openapitools.openapidiff.core.OpenApiCompare.fromSpecifications(OpenApiCompare.java:101)
	at org.openapitools.openapidiff.core.OpenApiCompare.fromLocations(OpenApiCompare.java:90)
	at org.openapitools.openapidiff.core.OpenApiCompare.fromLocations(OpenApiCompare.java:77)
	at org.openapitools.openapidiff.cli.Main.main(Main.java:156)

Which points to this line.

It seems openapi-diff expects the “right” schema to always be a oneOf with a mapping, but this is not the case here (only the “left” one is a oneOf).

julienrf added a commit to julienrf/openapi-diff that referenced this issue Jan 9, 2022
@joschi joschi added the bug label Feb 28, 2022
@joschi joschi added this to the 2.1.0 milestone Feb 28, 2022
joschi pushed a commit that referenced this issue Feb 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants