Validate a Json using the Path Response Schema in an Open API schema where the schema has a $ref (Potentially recursively)
I'm trying to accomplish something along the lines of:
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
SomeBuiltInSchemaParser.Load(schema.json);
var isValid = SomeBuiltInSchemaParser.IsValid(@"/pet/{petId}", "GET", content);
The long of it
I want to test (programmatically in C#) that an endpoint implementing a path returns a json that matches the schema defined in the responses section of the json for that path.
For example, given (heavily redacted, only essential):
{
"swagger": "2.0",
"info": {
"title": "Swagger Petstore",
"termsOfService": "http://swagger.io/terms/",
},
"host": "petstore.swagger.io",
"basePath": "/v2",
"schemes": [
"https",
"http"
],
"paths": {
"/pet/{petId}": {
"get": {
"tags": [
"pet"
],
"summary": "Find pet by ID",
"description": "Returns a single pet",
"operationId": "getPetById",
"produces": [
"application/json"
],
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet to return",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"$ref": "#/definitions/Pet"
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
}
},
"security": [
{
"api_key": []
}
]
},
},
},
"definitions": {
"Pet": {
"type": "object",
"required": [
"name",
"photoUrls"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"category": {
"$ref": "#/definitions/Category"
},
"name": {
"type": "string",
"example": "doggie"
},
"photoUrls": {
"type": "array",
"xml": {
"name": "photoUrl",
"wrapped": true
},
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"xml": {
"name": "tag",
"wrapped": true
},
"items": {
"$ref": "#/definitions/Tag"
}
},
"status": {
"type": "string",
"description": "pet status in the store",
"enum": [
"available",
"pending",
"sold"
]
}
},
},
"ApiResponse": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"type": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
},
"externalDocs": {
"description": "Find out more about Swagger",
"url": "http://swagger.io"
}
}
I have an HttpClient that calls [GET] /pet/{petId} and want to test the json content returned with the 200 status code that it satisfies the schema. Navigating to this path's response schema shows:
"$ref": "#/definitions/Pet"
Instead of being inline, it is a $ref. And could potentially be recursively ref, meaning navigating to #/definitions/Pet, its properties could also be $ref.
Is there a way to implement my above pseudocode with built in .Net or other robust 3rd party such as NewtonSoft? Or do I have to load the json schema and traverse all the $refs with custom code and put them together in the end for each path? I don't mind, but I have a feeling either .Net or Newtonsoft or something else have built-in (well-tested and already optimized) ways of doing this, just couldn't find it.
(Would greatly appreciate if someone already knows for this grinch might steal my Christmas :-))
What I have tried:
The following NewtonSoft library implementation works
JSchema schema = JSchema.Parse(jsonSchema);
JToken data = JToken.Parse(content);
IList<ValidationError> errors;
bool valid = data.IsValid(schema, out errors);
But jsonSchema in this instance is the already put together schema such as below
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"enum": [
"string",
"number",
"boolean",
"date",
"address",
"country",
"email",
"jason",
"url",
"image",
"signature",
"barcode",
"combined"
]
},
"name": {
"type": "string"
},
"static": {
"type": "boolean"
},
"default": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"metadata": {
"type": "string"
},
"required": {
"type": "boolean"
},
"optionList": {
"type": "string"
},
"combinedFieldNames": {
"type": "string"
},
"combinedFieldType": {
"type": "string"
},
"combinedFieldSeparator": {
"type": "string"
},
}
}
content
{
"name": "new_field",
"type": "string",
"required": false,
}