Skip to content

Commit

Permalink
[plugins/zdl-to-openapi] adds support for patch method
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangsa committed Dec 1, 2024
1 parent 7fe23b2 commit 9774ecb
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 31 deletions.
1 change: 1 addition & 0 deletions plugins/zdl-to-openapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jbang zw -p io.zenwave360.sdk.plugins.ZDLToOpenAPIPlugin \
| **Option** | **Description** | **Type** | **Default** | **Values** |
|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|---------------------------------------|------------|
| `specFile` | Spec file to parse | String | | |
| `baseOpenapi` | OpenAPI file to use as base. Generated API will be merged with this file. | String | | |
| `targetFolder` | Target folder to generate code to. If left empty, it will print to stdout. | File | | |
| `targetFile` | Target file | String | openapi.yml | |
| `title` | API Title | String | | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public class ZDLToOpenAPIGenerator implements Generator {
@DocumentedOption(description = "API Title")
public String title;

@DocumentedOption(description = "OpenAPI file to use as base. Generated API will be merged with this file.")
public String baseOpenapi;

@DocumentedOption(description = "DTO Suffix used for schemas in PATCH operations")
public String dtoPatchSuffix = "Patch";

@DocumentedOption(description = "Target file")
public String targetFile = "openapi.yml";
@DocumentedOption(description = "Extension property referencing original zdl entity in components schemas (default: x-business-entity)")
Expand All @@ -52,6 +58,7 @@ public class ZDLToOpenAPIGenerator implements Generator {
"get", 200,
"post", 201,
"put", 200,
"patch", 200,
"delete", 204
);

Expand Down Expand Up @@ -114,16 +121,35 @@ public List<TemplateOutput> generate(Map<String, Object> contextModel) {
JSONPath.set(oasSchemas, "components.schemas", schemas);

EntitiesToSchemasConverter converter = new EntitiesToSchemasConverter().withIdType(idType, idTypeFormat).withZdlBusinessEntityProperty(zdlBusinessEntityProperty);

var methodsWithRest = JSONPath.get(zdlModel, "$.services[*].methods[*][?(@.options.get || @.options.post || @.options.put || @.options.delete || @.options.patch)]", Collections.<Map>emptyList());
List<Map<String, Object>> entities = filterSchemasToInclude(zdlModel, methodsWithRest);
generateAndAddSchemas(entities, converter, zdlModel, schemas, listedEntities, paginatedEntities, "");

EntitiesToSchemasConverter converterForPatchOperation = new EntitiesToSchemasConverter().withIdType(idType, idTypeFormat).withUseNullableForAllFields(true, dtoPatchSuffix).withZdlBusinessEntityProperty(zdlBusinessEntityProperty);
var methodsWithPatch = JSONPath.get(zdlModel, "$.services[*].methods[*][?(@.options.patch)]", Collections.<Map>emptyList());
List<Map<String, Object>> entitiesForPatch = filterSchemasToInclude(zdlModel, methodsWithPatch);
generateAndAddSchemas(entitiesForPatch, converterForPatchOperation, zdlModel, schemas, listedEntities, paginatedEntities, dtoPatchSuffix);

String openAPISchemasString = null;
try {
openAPISchemasString = mapper.writeValueAsString(oasSchemas);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
// remove first line
openAPISchemasString = openAPISchemasString.substring(openAPISchemasString.indexOf("\n") + 1);

return List.of(generateTemplateOutput(contextModel, zdlToOpenAPITemplate, zdlModel, openAPISchemasString));
}

protected void generateAndAddSchemas(List<Map<String, Object>> entities, EntitiesToSchemasConverter converter, Map<String, Object> zdlModel, Map<String, Object> schemas, List<String> listedEntities, List<String> paginatedEntities, String dtoPatchSuffix) {
for (Map<String, Object> entity : entities) {
String entityName = (String) entity.get("name");
Map<String, Object> openAPISchema = converter.convertToSchema(entity, zdlModel);
schemas.put(entityName, openAPISchema);
schemas.put(entityName + dtoPatchSuffix, openAPISchema);

if(listedEntities.contains(entityName)) {
Map<String, Object> listSchema = Maps.of("type", "array", "items", Map.of("$ref", "#/components/schemas/" + entityName));
Map<String, Object> listSchema = Maps.of("type", "array", "items", Map.of("$ref", "#/components/schemas/" + entityName + dtoPatchSuffix));
schemas.put(entityName + "List", listSchema);
}

Expand All @@ -134,27 +160,10 @@ public List<TemplateOutput> generate(Map<String, Object> contextModel) {
Map.of(zdlBusinessEntityPaginatedProperty, entityName),
Map.of("properties",
Map.of("content",
Maps.of("type", "array", "items", Map.of("$ref", "#/components/schemas/" + entityName))))));
schemas.put(entityName + "Paginated", paginatedSchema);
Maps.of("type", "array", "items", Map.of("$ref", "#/components/schemas/" + entityName + dtoPatchSuffix))))));
schemas.put(entityName + dtoPatchSuffix + "Paginated", paginatedSchema);
}
}

// List<Map<String, Object>> enums = JSONPath.get(zdlModel, "$.enums[*]", emptyList());
// for (Map<String, Object> enumValue : enums) {
// Map<String, Object> enumSchema = converter.convertToSchema(enumValue, zdlModel);
// schemas.put((String) enumValue.get("name"), enumSchema);
// }

String openAPISchemasString = null;
try {
openAPISchemasString = mapper.writeValueAsString(oasSchemas);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
// remove first line
openAPISchemasString = openAPISchemasString.substring(openAPISchemasString.indexOf("\n") + 1);

return List.of(generateTemplateOutput(contextModel, zdlToOpenAPITemplate, zdlModel, openAPISchemasString));
}

protected List<Map<String, Object>> filterSchemasToInclude(Map<String, Object> model, List<Map> methodsWithCommands) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public class EntitiesToSchemasConverter {
public String idType = "string";
public String idTypeFormat = null;

public boolean useNullableForAllFields = false;
public String dtoPatchSuffix = "";

public boolean includeVersion = true;

public EntitiesToSchemasConverter withIdType(String idType) {
Expand Down Expand Up @@ -41,6 +44,12 @@ public EntitiesToSchemasConverter withZdlBusinessEntityProperty(String zdlBusine
return this;
}

public EntitiesToSchemasConverter withUseNullableForAllFields(boolean useNullableForAllFields, String dtoPatchSuffix) {
this.useNullableForAllFields = useNullableForAllFields;
this.dtoPatchSuffix = dtoPatchSuffix;
return this;
}

public Map<String, Object> convertToSchema(Map<String, Object> entityOrEnum, Map<String, Object> zdlModel) {
boolean isEnum = entityOrEnum.get("values") != null;
return isEnum ? convertEnumToSchema(entityOrEnum, zdlModel) : convertEntityToSchema(entityOrEnum, zdlModel);
Expand Down Expand Up @@ -89,15 +98,20 @@ public Map<String, Object> convertEntityToSchema(Map<String, Object> entity, Map
|| JSONPath.get(zdlModel, "$.events." + field.get("type")) != null;

if (isComplexType) {
property.put("$ref", "#/components/schemas/" + field.get("type"));
property.put("$ref", "#/components/schemas/" + field.get("type") + dtoPatchSuffix);
} else {
property.putAll(schemaTypeAndFormat((String) field.get("type")));
property.putAll(schemaTypeAndFormat((String) field.get("type") + dtoPatchSuffix));
}

String required = JSONPath.get(field, "$.validations.required.value");
if (required != null) {
requiredProperties.add((String) field.get("name"));
if(useNullableForAllFields) {
property.put("nullable", true);
} else {
String required = JSONPath.get(field, "$.validations.required.value");
if (required != null) {
requiredProperties.add((String) field.get("name"));
}
}

String minlength = JSONPath.get(field, "$.validations.minlength.value");
if (minlength != null) {
property.put("minLength", asNumber(minlength));
Expand Down Expand Up @@ -143,7 +157,7 @@ public Map<String, Object> convertEntityToSchema(Map<String, Object> entity, Map
var readOnlyWarning = isAddRelationshipById ? "(read-only) " : "";
// TODO desc+$ref: property.put("description", readOnlyWarning + relationship.getOrDefault("comment", ""));
}
property.put("$ref", "#/components/schemas/" + relationship.get("otherEntityName"));
property.put("$ref", "#/components/schemas/" + relationship.get("otherEntityName") + dtoPatchSuffix);
if (isCollection) {
property = Maps.of("type", "array", "items", property);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ public static String getRequestBodyType(Map<String, Object> method, Map apiModel

public static Map<String, Object> getHttpOption(Map method) {
var get = JSONPath.get(method, "$.options.get");
var put = JSONPath.get(method, "$.options.put");
var post = JSONPath.get(method, "$.options.post");
var put = JSONPath.get(method, "$.options.put");
var patch = JSONPath.get(method, "$.options.patch");
var delete = JSONPath.get(method, "$.options.delete");
var httpOptions = ObjectUtils.firstNonNull(get, put, post, delete);
var httpMethod = get != null? "get" : put != null? "put" : post != null? "post" : delete != null? "delete" : null;
var httpOptions = ObjectUtils.firstNonNull(get, put, post, patch, delete);
var httpMethod = get != null? "get" : put != null? "put" : post != null? "post" : delete != null? "delete" : patch != null? "patch" : null;
if (httpMethod == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ service CustomerService for (Customer) {
@asyncapi({ api: ThirdPartyAPI, operationId: "doUpdateCustomer"})
updateCustomer(id, CustomerInput) Customer? withEvents CustomerEvent CustomerUpdated /** update customer javadoc comment */

@patch("/customers/{customerId}")
patchCustomer(id, CustomerInput) Customer? withEvents CustomerEvent CustomerUpdated /** update customer javadoc comment */

@delete("/customers/{customerId}")
deleteCustomer(id) withEvents CustomerDeleted

Expand Down

0 comments on commit 9774ecb

Please sign in to comment.