I am implementing a web application in Java and SpringBoot (angular and Typescript)
We decided to go for an "API first" approach and we design our API in openAPI 3.1 format. These yaml files are then used to generate java code that implements the api model.
now, for each resource we would like to return the jsonSchema of requests and response.
What approach can i use it? Should i parse the yaml and extract and convert the schemas manually or is there a framework that could help?
As an additional step we would like to edit the schemas by inserting some dynamic values and constraints.
Thanks
I didn't find a standard solution for this problem. But implementing it just takes a few lines of code.
As suggested by Carsten one way is to use jsonschema-generator and use the classes OpenAPI generator created to generate the JSONSchema.
The approach I used is to used is to use the OpenAPI library itself.
private OpenAPI parseOpenAPIDefinitionFromFile(final ClassPathResource apiResource) {
LOG.info(apiResource.getPath());
OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
ParseOptions options = new ParseOptions();
options.setResolve(true);
options.setResolveFully(true);
return openApiParser.read(apiResource.getPath(), null, options);
}
the created object can be navigated via path, Operations, media types and responses.
In my case I decided to extract operations, place them in a map using their ID as key.
public Map<String, Operation> getOperationIDToOperationMap(final ClassPathResource apiPath) {
var apiJsonSchema = this.getJsonSchemaForApi(apiPath);
Map<String, Operation> operationIdToOperationMap = new HashMap<>();
for (PathItem path : apiJsonSchema.getPaths().values()) {
for (Operation currentOperation : path.readOperations()) {
operationIdToOperationMap.put(currentOperation.getOperationId(), currentOperation);
}
}
return operationIdToOperationMap;
}
The Controller that is returning these schemas, uses a service that fills the enum values from the DB, So i can validate user input and provide dropdown values.
addDropdownValues(schema, "contractType.contractTypeName", this.contractTypeEntityRepository.findAll(), ContractTypeEntity::getContractTypeName);
protected static <T> void addDropdownValues(final Schema schema, final String searchKey, final List<T> itemsToAdd, final Function<T, String> mapFunction) {
Schema targetSchema = getTargetSchema(schema, searchKey);
addItemsToEnum(targetSchema, itemsToAdd, mapFunction);
}
protected static Schema getTargetSchema(final Schema rootSchema, final String searchKey) {
Schema targetSchema = rootSchema;
StringTokenizer tokenizer = new StringTokenizer(searchKey, ".");
while (tokenizer.hasMoreElements()) {
var token = tokenizer.nextToken();
if (token.endsWith("[]")) {
targetSchema = getItems(targetSchema);
} else {
targetSchema = getProperties(targetSchema, token);
}
}
return targetSchema;
}
protected static <T> void addItemsToEnum(final Schema dropdown, final List<T> allItems, final Function<T, String> mapFunction) {
List<String> enumItems = new ArrayList<>();
for (var enumItem : allItems) {
enumItems.add(mapFunction.apply(enumItem));
}
dropdown.setEnum(enumItems);
}
The code is not complete but these are the most important bits in my opinion. I hope that helps.