Validating multiple subsequent requests with "discriminator" in the same validator instance behaves incorrectly
Hi,
The validation result becomes inconsistent when validating schema involves "discirminator" field. Take the following json:
{
"swagger": "2.0",
"basePath": "/",
"paths": {
"/create": {
"post": {
"consumes": [
"application/json"
],
"parameters": [
{
"$ref": "#/parameters/petRequest"
}
],
"responses": {
"201": {
"$ref": "#/responses/petResponse"
}
}
}
}
},
"definitions": {
"Pet": {
"type": "object",
"discriminator" : "petType",
"properties": {
"name": {
"type": "string"
},
"petType": {
"type": "string"
}
},
"required": [
"name",
"petType"
]
},
"Dog": {
"allOf": [
{
"$ref": "#/definitions/Pet"
},
{
"type": "object",
"properties": {
"packSize": {
"type": "integer",
"format": "int32"
}
},
"required": [
"packSize"
]
}
]
}
},
"createPetResponse": {
"type": "object"
},
"parameters": {
"petRequest": {
"name": "petRequest",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Pet"
}
}
},
"responses": {
"petResponse": {
"schema": {
"$ref": "#/definitions/createPetResponse"
}
}
}
}
where "Pet" schema has the attribute "discriminator"
"discriminator" : "petType"
So if I send the following request (with wrong packSize value, should be an int32):
@Test
public void singleRequest() {
final Request request = SimpleRequest.Builder.post("/create")
.withBody("{\"name\":\"Kiki\",\"petType\":\"Dog\",\"packSize\":\"not-a-number\"}")
.build();
final ValidationReport report = validator.validateRequest(request);
assertTrue(report.hasErrors());
}
Test passes and report has key & message:
Key: validation.schema.discriminator : Failed validation of discriminator schema '/definitions/Dog'
It's not clear what exactly failing when validating "Dog", but let's accept it for now.
Now, if we send two identical requests back to back:
@Test
public void doubleRequestsWithoutValidatorRecreation() {
final Request request1 = SimpleRequest.Builder.post("/create")
.withBody("{\"name\":\"Kiki\",\"petType\":\"Dog\",\"packSize\":\"not-a-number\"}")
.build();
final ValidationReport report1 = validator.validateRequest(request1);
assertTrue(report1.hasErrors());
final Request request2 = SimpleRequest.Builder.post("/create")
.withBody("{\"name\":\"Kiki\",\"petType\":\"Dog\",\"packSize\":\"not-a-number\"}")
.build();
final ValidationReport report2 = validator.validateRequest(request2);
assertTrue(report2.hasErrors());
}
In theory, two requests should fail with the same validation error, but the second request went through, this test fails here:
assertTrue(report2.hasErrors());
Interestingly, if take this third test:
@Test
public void doubleRequestsWithValidatorRecreation() {
final Request request1 = SimpleRequest.Builder.post("/create")
.withBody("{\"name\":\"Kiki\",\"petType\":\"Dog\",\"packSize\":\"not-a-number\"}")
.build();
final ValidationReport report1 = validator.validateRequest(request1);
assertTrue(report1.hasErrors());
recreateValidator();
final Request request2 = SimpleRequest.Builder.post("/create")
.withBody("{\"name\":\"Kiki\",\"petType\":\"Dog\",\"packSize\":\"not-a-number\"}")
.build();
final ValidationReport report2 = validator.validateRequest(request2);
assertTrue(report2.hasErrors());
}
then it passes, which mean the validator fails both requests (expected).
recreateValidator:
public void recreateValidator() {
validator = SwaggerRequestResponseValidator.createFor("/oai/pet-api.json")
.withLevelResolver(
LevelResolver.create()
.withLevel("validation.schema.additionalProperties", ValidationReport.Level.IGNORE)
.build())
.build();
}
To me, the behaviour of the second test is an issue. Eventhough, there is a workaround as the third one suggests, but it may be expensive to create the validator for every request ?
I don't want to speculate but it seems once it fails to validate the schema specified in "discriminator", it ignores that schema in future validation ?
Or perhaps, there is something about the validation that I dont understand here.
Either way, I'd love an explanation.
Thanks, Tuan
Comments (8)
-
reporter -
Thanks Tuan. I'll try to take a look at this next week.
It is definitely a bug if there is some sort of state being maintained between validation executions.
-
Actually - I know what's going on. During validation we remove the
discriminator
keyword to avoid a validation loop, and then never put it back in (facepalm), so subsequent requests never see the discriminator keyword and so never validate against it.I think the quickest fix is probably to re-add the keyword before leaving the validate method, but this probably has problems with multiple threads using the validator at the same time. Ideally we could use a thread-safe way to detect the validation cycle (maybe threadlocal storage or something like that).
-
- changed title to Validating multiple subsequent requests with "discriminator" in the same validator instance behaves incorrectly
Updated issue title
-
reporter Thanks James,
It explains a lot. Why do we have to remove the keyword again ? If it results in a validation loop, is that more towards the schema is wrongly configured ?
Can we make "a copy" of the schema for each validation method while keeping the original unchanged ?
Anyway, I'm throwing stuff out without much context/understanding what the validator is doing around that. Just wondering how do we coordinate the effort to resolve this issue.
Cheers, Tuan
-
- changed status to open
-
-
assigned issue to
-
assigned issue to
-
- changed status to resolved
Available in v1.1.0
- Log in to comment
I've pushed the test cases showed above in my forked repo for reference...