Validation Failing For Swagger with Inheritance and Polymorphism (including both oneOf & allOf)
We are trying to validate an API which includes a set of addresses described by using oneOf keyword and then followed by discriminator & mapping. The mapping fields include values with allOf Keyword.
The swagger document is rendered fine on Swagger UI, anyhow the actual request for validating the swagger fails with the attached error.
URI : /bitBucketIssue/v1/user/v1/accountregister/
BaseURI: /bitBucketIssue/v1/
SwaggerURL(which is to be validated): user/v1/accountregister/
PFA ValidationReport, Error details, YAML file & Request used to validate the document.
Comments (19)
-
reporter -
https://bitbucket.org/atlassian/swagger-request-validator/src/master/docs/FAQ.md
Have you read this FAQ,
-
reporter This FAQ suggestion was already been considered and implemented, we have programmatically opted to ignore the additional properties.
PFB the attachment.
-
Using:
<groupId>com.atlassian.oai</groupId> <artifactId>swagger-request-validator-core</artifactId> <version>2.11.0</version>
And this validator:
OpenApiInteractionValidator validator = OpenApiInteractionValidator
.createFor(OPENAPI_LOCATION)
.withLevelResolver(LevelResolverFactory.withAdditionalPropertiesIgnored())
.build();
I am getting the following error in your situation:
'/personal_data/addresses/0'] Instance failed to match exactly one schema (matched 3 out of 3)
com.nn.aav.insurance.nonlife.insurancepackage.business.validation.boundary.OpenAPIValidationException: Validation failed.
[ERROR][REQUEST][POST user/v1/accountregister @body] [Path '/personal_data/addresses/0'] Instance failed to match exactly one schema (matched 3 out of 3)
Which is correct because it can match all of the items.
You can do two things:
- Dont use the AbstractAddress. So copy it to each child. So you can set
additionalProperties: false
. - Another thing you can do which is very easy to do is make a property required in each child which is unique.
So in the last example:
{
"personal_data": {
"firstName": "Demo",
"lastName": "Demo",
"addresses": [
{
"country": "Demo",
"type": "ADDRESS_TYPE:v3.address.GermanAddress",
"usageType": "ADDRESS_USAGE_TYPE:HOME_ADDRESS",
"primary": true,
"uuid": "123456789",
"addressAddition": "",
"cityGeneral": "Demo",
"housenumber": "1234567",
"street": "Demo",
"zipCode": "12345678",
"name": "Demo"
}
]
},
"country": "Demo"
}
Using swagger:
{ "openapi": "3.0.2", "info": { "title": "Account", "version": "1.0.0" }, "paths": { "/user/v1/accountregister/": { "post": { "tags": [ "Personal Data" ], "summary": "Store a new account", "description": "", "operationId": "registerAccount", "requestBody": { "$ref": "#/components/requestBodies/RegisterUserModel" }, "responses": { "201": { "description": "Account registered successfully.", "content": { "application/json": { "examples": { "response": { "value": { "hasError": false, "errorCategory": "", "errorList": null } } } } } } } } } }, "components": { "requestBodies": { "RegisterUserModel": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterUserModel" } } }, "required": true } }, "schemas": { "AbstractAddress": { "description": "Each of the concrete Addresses have the following attributes in common.", "type": "object", "required": [ "type", "country", "usageType", "primary", "uuid" ], "properties": { "country": { "type": "string" }, "type": { "type": "string" }, "usageType": { "type": "string" }, "primary": { "type": "boolean" }, "uuid": { "type": "string" } } }, "GeneralAddress": { "description": "General address, used for Countries which do not have a specialized address format.", "required": [ "cityGeneral" ], "allOf": [ { "$ref": "#/components/schemas/AbstractAddress" }, { "type": "object", "properties": { "addressAddition": { "type": "string", "nullable": true }, "cityGeneral": { "type": "string", "nullable": true }, "housenumber": { "type": "string", "nullable": true }, "street": { "type": "string", "nullable": true }, "zipCode": { "type": "string", "nullable": true }, "name": { "type": "string", "nullable": true } } } ] }, "AustralianAddress": { "description": "Address format for Australian addresses.", "required": [ "cityAussie" ], "allOf": [ { "$ref": "#/components/schemas/AbstractAddress" }, { "type": "object", "properties": { "addressLine1": { "type": "string", "nullable": true }, "addressLine2": { "type": "string", "nullable": true }, "state": { "type": "string", "nullable": true }, "cityAussie": { "type": "string", "nullable": true }, "zipCode": { "type": "string", "nullable": true }, "name": { "type": "string", "nullable": true } } } ] }, "HongKongAddress": { "description": "Address format for addresses located in Hong Kong", "required": [ "addressLine3" ], "allOf": [ { "$ref": "#/components/schemas/AbstractAddress" }, { "type": "object", "properties": { "addressLine1": { "type": "string", "nullable": true }, "addressLine2": { "type": "string", "nullable": true }, "addressLine3": { "type": "string", "nullable": true }, "name": { "type": "string", "nullable": true } } } ] }, "PersonalData": { "description": "Data of a single user profile", "type": "object", "required": [ "firstName", "lastName" ], "properties": { "firstName": { "description": "The given name of a person.", "type": "string", "nullable": true }, "lastName": { "description": "The surname of a person.", "type": "string", "nullable": true }, "addresses": { "description": "The list of addresses", "type": "array", "items": { "oneOf": [ { "$ref": "#/components/schemas/GeneralAddress" }, { "$ref": "#/components/schemas/AustralianAddress" }, { "$ref": "#/components/schemas/HongKongAddress" } ], "discriminator": { "propertyName": "type", "mapping": { "ADDRESS_TYPE:v3.address.AustralianAddress": "#/components/schemas/AustralianAddress", "ADDRESS_TYPE:v3.address.HongKongAddress": "#/components/schemas/HongKongAddress", "ADDRESS_TYPE:v3.address.GermanAddress": "#/components/schemas/GeneralAddress" } } } } } }, "RegisterUserModel": { "type": "object", "required": [ "country", "personal_data" ], "properties": { "personal_data": { "$ref": "#/components/schemas/PersonalData" }, "country": { "type": "string", "nullable": false, "description": "Country to which user belongs." } } } } } }
- Dont use the AbstractAddress. So copy it to each child. So you can set
-
- changed status to resolved
A fix to the related problem
#336has been released in v2.19.3.Please re-open this issue if the problem persists in the latest version.
Thanks.
-
reporter Hi James,
The issue still exists, I have tried with 2.12.0, 2.19.3 & 2.19.4, getting the same error again.Please have a look once.
-
reporter - changed status to open
-
reporter Hi James, Any Updates on the issue?
-
reporter Hi James/Team,
The issue still exists, it still fails with the same exception as attached, tested with the latest version. Please check once.
-
@Shubham Mahindru We momentaly solved by stripping off the AdditionalPropertiesInjectionTransformer out of the list of the SchemaValidator transformer, forking the library and making the list of transformers public.
-
reporter Hi
Can you please share what is the intended purpose of the code change ?
This is the following code which you are mentioning, so what other impacts would it have on the validator, because with the latest version even the problem exists.private final List<SchemaTransformer> transformers = Arrays.asList(SchemaDefinitionsInjectionTransformer.getInstance(), SchemaRefInjectionTransformer.getInstance(), AdditionalPropertiesInjectionTransformer.getInstance(), RequiredFieldTransformer.getInstance());
-
reporter Hi,
With the changes suggested, now its matching multiple schemas
{ "error": "400",
"error_description": "Validation failed.\n[ERROR][REQUEST][POST /check3/v1/account/v1/register @body] [Path '/personal_data/addresses/0'] Instance failed to match exactly one schema (matched 9 out of 9)"
}
Please suggest as it still complains about the validation part.
-
Hi,
Any Update on this. We are still facing issues with the latest releases.
-
Hi Guys,
Please help us understand If this will be delivered in sometime future and ETA on that please.
-
Hi Sri,
Unfortunately I can’t give a commitment on when a fix to this specific problem will be available. If you could include example spec + request for your scenario it would help. I am also more than happy to review PRs if you are able to identify and fix the cause.
Cheers.
-
Hi
I guess i run into the same problem at least with allOf. It seems that the two scmeas are not merge into one before the validation when i interpret the error message correctly.
2022-09-24 11:36:47.129 ERROR 22248 --- [qtp373973695-40] c.a.o.v.s.DefaultValidationReportHandler : OpenAPI location=REQUEST key=POST#/private/v1/oa3/test levels=ERROR messages=Validation failed. [ERROR][REQUEST][POST /private/v1/oa3/test @body] [Path '/obj'] Instance failed to match all required schemas (matched only 0 out of 2) * /properties/obj/allOf/0: Object instance has properties which are not allowed by the schema: ["_type","str"] * /properties/obj/allOf/1: Object instance has properties which are not allowed by the schema: ["base"] - [ERROR][] [Path '/obj'] Object instance has properties which are not allowed by the schema: ["_type","str"] - [ERROR][] [Path '/obj'] Object instance has properties which are not allowed by the schema: ["base"]
The OpenApi Spec is quite simple
openapi: 3.0.0 info: title: OA3 Validation Test Service version: "1.0" servers: - url: "http://localhost:8080/oa3" components: schemas: TestRequest: type: object required: - obj properties: obj: $ref: '#/components/schemas/ObjectA' BaseObject: type: object required: - base properties: base: type: string ObjectA: allOf: - $ref: '#/components/schemas/BaseObject' - type: object required: - _type - str properties: _type: type: string str: type: string TestResponse: type: object required: - value properties: value: type: string paths: /test: post: operationId: test requestBody: content: application/json: schema: $ref: '#/components/schemas/TestRequest' responses: 200: description: OK with the result of the executed query content: application/json: schema: $ref: '#/components/schemas/TestResponse'
And here also the requets body:
{ "obj": { "base": "test base", "_type": "OBJECT_A", "str": "12345" } }
Hope this helps to identify the problem
-
Also struggled with this. For us the following setting made
allOf
(similar setup as Reto Urfer) work:.withResolveCombinators(true)
This also seems to be the recommended approach mentioned in the FAQ.
Still it would be nice if it would work without it :). As I am not sure if this has any side effects.
-
The problem we are still experiencing is:
Instance failed to match exactly one schema (matched 6 out of 6)
. -
i did some analysis and it seems that the problems with oneOf are completely different from the problems with allOf. Like Patrick Boos mentioned, the problems with allOf can be solved be defining
.withResolveCombinators(true)
. The problems with oneOf are described in the following issues #289, #334 and #349. All of them are still open and marked as major problems - Log in to comment
Hi James,
Did you get some time to check for the issue? Please check once for the issue, we are totally blocked and can’t proceed further without a permanent fix