All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.zenwave360.generator.plugins.JDLToOpenAPIGenerator Maven / Gradle / Ivy

package io.zenwave360.generator.plugins;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import io.zenwave360.generator.doc.DocumentedOption;
import io.zenwave360.generator.generators.AbstractJDLGenerator;
import io.zenwave360.generator.generators.JDLEntitiesToSchemasConverter;
import io.zenwave360.generator.templating.HandlebarsEngine;
import io.zenwave360.generator.templating.OutputFormatType;
import io.zenwave360.generator.templating.TemplateEngine;
import io.zenwave360.generator.templating.TemplateInput;
import io.zenwave360.generator.templating.TemplateOutput;
import io.zenwave360.generator.utils.JSONPath;
import io.zenwave360.generator.utils.Maps;

public class JDLToOpenAPIGenerator extends AbstractJDLGenerator {

    ObjectMapper mapper = new ObjectMapper(new YAMLFactory());

    public String sourceProperty = "jdl";

    @DocumentedOption(description = "Entities to generate code for")
    public List entities = new ArrayList<>();

    @DocumentedOption(description = "Skip generating operations for entities annotated with these")
    public List annotationsToGenerate = List.of("aggregate");
    @DocumentedOption(description = "Skip generating operations for entities annotated with these")
    public List skipForAnnotations = List.of("vo", "embedded", "skip");

    @DocumentedOption(description = "Target file")
    public String targetFile = "openapi.yml";
    @DocumentedOption(description = "Extension property referencing original jdl entity in components schemas (default: x-business-entity)")
    public String jdlBusinessEntityProperty = "x-business-entity";

    @DocumentedOption(description = "Extension property referencing original jdl entity in components schemas for paginated lists")
    public String jdlBusinessEntityPaginatedProperty = "x-business-entity-paginated";

    @DocumentedOption(description = "JSONPath list to search for response DTO schemas for list or paginated results. Examples: '$.items' for lists or '$.properties..items' for paginated results.")
    public List paginatedDtoItemsJsonPath = List.of("$.items", "$.properties.content.items");

    @DocumentedOption(description = "Suffix for search criteria DTOs (default: Criteria)")
    public String criteriaDTOSuffix = "Criteria";

    @DocumentedOption(description = "JsonSchema type for id fields and parameters.")
    public String idType = "string";

    @DocumentedOption(description = "JsonSchema type format for id fields and parameters.")
    public String idTypeFormat = null;

    public JDLToOpenAPIGenerator withSourceProperty(String sourceProperty) {
        this.sourceProperty = sourceProperty;
        return this;
    }

    private HandlebarsEngine handlebarsEngine = new HandlebarsEngine();

    private final TemplateInput jdlToOpenAPITemplate = new TemplateInput("io/zenwave360/generator/plugins/OpenAPIToJDLGenerator/JDLToOpenAPI.yml", "{{targetFile}}").withMimeType(OutputFormatType.YAML);

    protected Map getJDLModel(Map contextModel) {
        return (Map) contextModel.get(sourceProperty);
    }

    protected boolean isGenerateSchemaEntity(Map entity) {
        String entityName = (String) entity.get("name");
        return entities.isEmpty() || entities.contains(entityName);
    }

    {
        handlebarsEngine.getHandlebars().registerHelper("skipOperations", (context, options) -> {
            Map entity = (Map) context;
            String annotationsFilter = annotationsToGenerate.stream().map(a -> "@." + a).collect(Collectors.joining(" || "));
            String skipAnnotationsFilter = skipForAnnotations.stream().map(a -> "@." + a).collect(Collectors.joining(" || "));
            boolean isGenerate = annotationsToGenerate.isEmpty() || !((List) JSONPath.get(entity, "$.options[?(" + annotationsFilter + ")]")).isEmpty();
            boolean isSkip = skipForAnnotations.isEmpty() || !((List) JSONPath.get(entity, "$.options[?(" + skipAnnotationsFilter + ")]")).isEmpty();
            return !isGenerate || isSkip;
        });
    }

    @Override
    public List generate(Map contextModel) {
        Map jdlModel = getJDLModel(contextModel);
        List serviceNames = JSONPath.get(jdlModel, "$.options.options.service[*].value");
        ((Map) jdlModel).put("serviceNames", serviceNames);

        Map oasSchemas = new HashMap<>();
        Map schemas = new LinkedHashMap<>();
        JSONPath.set(oasSchemas, "components.schemas", schemas);

        JDLEntitiesToSchemasConverter converter = new JDLEntitiesToSchemasConverter().withIdType(idType, idTypeFormat).withJdlBusinessEntityProperty(jdlBusinessEntityProperty);

        List> entities = (List) JSONPath.get(jdlModel, "$.entities[*]");
        for (Map entity : entities) {
            if (!isGenerateSchemaEntity(entity)) {
                continue;
            }
            String entityName = (String) entity.get("name");
            Map openAPISchema = converter.convertToSchema(entity, jdlModel);
            schemas.put(entityName, openAPISchema);

            Map paginatedSchema = new HashMap<>();
            paginatedSchema.put("allOf", List.of(
                    Map.of("$ref", "#/components/schemas/Page"),
                    Map.of(jdlBusinessEntityPaginatedProperty, entityName),
                    Map.of("properties",
                            Map.of("content",
                                    Maps.of("type", "array", "items", Map.of("$ref", "#/components/schemas/" + entityName))))));
            schemas.put(entityName + "Paginated", paginatedSchema);
        }

        List> enums = (List) JSONPath.get(jdlModel, "$.enums.enums[*]");
        for (Map enumValue : enums) {
            if (!isGenerateSchemaEntity(enumValue)) {
                continue;
            }
            Map enumSchema = converter.convertToSchema(enumValue, jdlModel);
            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, jdlToOpenAPITemplate, jdlModel, openAPISchemasString));
    }

    public TemplateOutput generateTemplateOutput(Map contextModel, TemplateInput template, Map jdlModel, String schemasAsString) {
        Map model = new HashMap<>();
        model.putAll(this.asConfigurationMap());
        model.put("context", contextModel);
        model.put("jdlModel", jdlModel);
        model.put("schemasAsString", schemasAsString);
        return getTemplateEngine().processTemplate(model, template).get(0);
    }

    protected TemplateEngine getTemplateEngine() {
        handlebarsEngine.getHandlebars().registerHelper("asTagName", (context, options) -> {
            if (context instanceof String) {
                return ((String) context).replaceAll("(Service|UseCases)", "");
            }
            return "Default";
        });
        return handlebarsEngine;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy