Reputation: 2067
I need to build an API to validate the request body against the registered schema for the respective type
& subType
.
API Contract:
{
"id": "<any-uuid>",
"type": "<some-type>",
"subType": "<some-sub-type>",
"data": {
}
}
Here, OpenAPI schema will be fetched based on the type
and subType
and then need to validate the data
element against the respective OpenAPI schema.
Wrote the below snippet:
Map<String, Object> data = //get the data object from API request body;
JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V7);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = objectMapper.convertValue(data, JsonNode.class);
String schemaJson = // fetch the registered schema for type and subtype
JsonSchema schema = jsonSchemaFactory.getSchema(schemaJson);
Set<ValidationMessage> errors = schema.validate(node);
// Throw exception when errors present in the Json Payload
if (errors.size() > 0) {
// throw the exception with errors
}
This code is working, when the schema don't have:
openapi
, paths
, info
, components
.{
"openapi": "3.0.0",
"paths": {},
"info": {
"title": "Patient Info API",
"version": "v0.1.0"
},
"components": {
"schemas": {
"Data": {
"type": "object",
"required": [
"action",
"patient"
],
"properties": {
"action": {
"type": "string",
"enum": [
"ADMIT",
"DISCHARGE",
"TRANSFER"
]
},
"patient": {
"$ref": "#/components/schemas/Patient"
}
}
},
"Patient": {
"type": "object",
"required": [
"firstName",
"lastName"
],
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
}
}
}
}
}
data
element in the API request body looks like this.{
"action": "ADMIT",
"patient": {
"firstName": "John",
"lastName": "Doe"
}
}
Can json-schema-validator help to achieve this?
Upvotes: 0
Views: 3804
Reputation: 2067
For simplicity, wrote all the logic inside the Spring Controller itself.
package com.schema.validator.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.io.Files;
import com.networknt.schema.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A REST API to validate the request body against Open API Schema
*
*/
@RestController
@RequestMapping("schema-validate")
public class SchemaValidatorController {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JsonSchemaFactory jsonSchemaFactory =
JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V6);
@Value("classpath:schema/json-schema.json")
private Resource schemaResource;
@PostMapping
public ResponseEntity<List<String>> validateSchema(@RequestBody Map<String, Object> jsonRequest) throws IOException {
String schemaJson = Files.asCharSource(schemaResource.getFile(), Charset.defaultCharset()).read();
final JsonNode requestJsonNode = objectMapper.valueToTree(jsonRequest);
JsonNode componentNode = objectMapper.readTree(schemaJson).get("components");
// Get first name in the JsonNode
JsonNode schemaNode = componentNode.get("schemas").elements().next();
((ObjectNode) schemaNode).set("components", componentNode);
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
config.setTypeLoose(false);
config.setHandleNullableField(true);
JsonSchema jsonSchema = jsonSchemaFactory.getSchema(schemaNode, config);
Set<ValidationMessage> errors =
jsonSchema.validate(requestJsonNode, requestJsonNode, "data");
if (CollectionUtils.isEmpty(errors)) {
return ResponseEntity.ok(Collections.emptyList());
}
return ResponseEntity.ok(errors.stream()
.map(ValidationMessage::getMessage)
.collect(Collectors.toList()));
}
}
Here, json-schema.json
created under resources/schema
folder.
Limitation: The root element in Open API schema should be declared as a first child of schemas
in Open API spec.
Upvotes: 0