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

org.openapitools.codegen.languages.KotlinMiskServerCodegen Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openapitools.codegen.languages;

import lombok.Setter;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.languages.features.BeanValidationFeatures;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.DocumentationFeature;
import org.openapitools.codegen.meta.features.GlobalFeature;
import org.openapitools.codegen.meta.features.ParameterFeature;
import org.openapitools.codegen.meta.features.SchemaSupportFeature;
import org.openapitools.codegen.meta.features.SecurityFeature;
import org.openapitools.codegen.meta.features.WireFormatFeature;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static org.openapitools.codegen.utils.StringUtils.camelize;

public class KotlinMiskServerCodegen extends AbstractKotlinCodegen implements BeanValidationFeatures {

    private final Logger LOGGER = LoggerFactory.getLogger(KotlinMiskServerCodegen.class);

    public static final String MODULE_CLASS_NAME = "moduleClassName";
    public static final String ACTION_PATH_PREFIX = "actionPathPrefix";

    private static final String ROOT_PACKAGE = "rootPackage";
    public static final String GENERATE_STUB_IMPL_CLASSES = "generateStubImplClasses";
    public static final String ADD_MODEL_MOSHI_JSON_ANNOTATION = "addModelMoshiJsonAnnotation";
    public static final String ACTION_ANNOTATIONS = "actionAnnotations";
    public static final String ACTION_IMPORTS = "actionImports";
    public static final String ACTION_PARENT_CLASS = "actionParentClass";
    public static final String ACTION_REQUEST_CONTENT_TYPE = "actionRequestContentType";
    public static final String ACTION_REQUEST_CONTENT_TYPE_PREFIX = "actionRequestContentTypePrefix";
    public static final String TESTING_MODULE = "testingModule";
    private static final String TESTING_MODULE_NAME = "testingModuleName";

    private boolean useBeanValidation = true;

    @Setter
    private boolean generateStubImplClasses = false;

    @Setter
    private boolean addModelMoshiJsonAnnotation = true;

    protected String rootPackage = "org.openapitools.server.api";
    protected String apiVersion = "1.0.0-SNAPSHOT";

    @Setter protected String moduleClassName = "OpenApiModule";
    @Setter protected String actionPathPrefix = "";
    @Setter protected List actionAnnotations =
            List.of("@LogRequestResponse(bodySampling = 1.0, errorBodySampling = 1.0)");
    @Setter protected List actionImports =
            List.of("misk.web.actions.WebAction","misk.web.interceptors.LogRequestResponse");
    @Setter protected String actionParentClass = "WebAction";
    @Setter protected String actionRequestContentType = "@RequestContentType";
    @Setter protected String actionRequestContentTypePrefix = "MediaTypes";
    @Setter protected String testingModule = "misk.testing.MiskTestModule";
    @Setter protected String testingModuleName;

    @Override
    public CodegenType getTag() {
        return CodegenType.SERVER;
    }

    @Override
    public String getName() {
        return "kotlin-misk";
    }

    @Override
    public String getHelp() {
        return "Generates a kotlin-misk server.";
    }

    public KotlinMiskServerCodegen() {
        super();

        addSwitch(USE_BEANVALIDATION, "Use BeanValidation API annotations to validate data types", useBeanValidation);
        addSwitch(GENERATE_STUB_IMPL_CLASSES, "Generate Stub Impl Classes", generateStubImplClasses);
        addSwitch(ADD_MODEL_MOSHI_JSON_ANNOTATION, "Add a Moshi JSON adapter annotation to all model classes", addModelMoshiJsonAnnotation);

        modifyFeatureSet(features -> features
                .includeDocumentationFeatures(DocumentationFeature.Readme)
                .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.PROTOBUF))
                .securityFeatures(EnumSet.noneOf(
                        SecurityFeature.class
                ))
                .excludeGlobalFeatures(
                        GlobalFeature.XMLStructureDefinitions,
                        GlobalFeature.Callbacks,
                        GlobalFeature.LinkObjects,
                        GlobalFeature.ParameterStyling
                )
                .excludeSchemaSupportFeatures(
                        SchemaSupportFeature.Polymorphism
                )
                .includeParameterFeatures(
                        ParameterFeature.Cookie
                )
        );

        embeddedTemplateDir = templateDir = "kotlin-misk";

        generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
                .stability(Stability.STABLE)
                .build();

        outputFolder = "generated-code" + File.separator + "kotlin-misk";

        addOption(MODULE_CLASS_NAME, "Name of the generated module class", moduleClassName);
        addOption(ACTION_PATH_PREFIX, "Prefix for action path", actionPathPrefix);
        addOption(ACTION_IMPORTS, "String Imports for Actions separated by a semicolon(;)", String.join(";", actionImports));
        addOption(ACTION_ANNOTATIONS, "String Annotations for Actions separated by a semicolon(;)", String.join(";", actionAnnotations));
        addOption(ACTION_PARENT_CLASS, "Parent Class for Action", actionParentClass);
        addOption(ACTION_REQUEST_CONTENT_TYPE, "Request ContentType for Action", actionRequestContentType);
        addOption(ACTION_REQUEST_CONTENT_TYPE_PREFIX, "Request ContentType Prefix for Action", actionRequestContentTypePrefix);
        addOption(TESTING_MODULE, "Testing module class", testingModule);

        apiTestTemplateFiles.clear();
        apiTestTemplateFiles.put("api_test.mustache", ".kt");

        apiDocTemplateFiles.clear();
        apiDocTemplateFiles.put("api_doc.mustache", ".md");

        modelDocTemplateFiles.clear();
        modelDocTemplateFiles.put("model_doc.mustache", ".md");

        supportingFiles.clear();

        apiTemplateFiles.clear();
        apiTemplateFiles.put("apiAction.mustache", "Action.kt");

        if (generateStubImplClasses) {
            apiTemplateFiles.put("apiImpl.mustache", "Impl.kt");
            apiTemplateFiles.put("apiInterface.mustache", ".kt");
        }

        modelTemplateFiles.put("model.mustache", ".kt");

        apiPackage = rootPackage + ".api";
        modelPackage = rootPackage + ".model";
        artifactId = "openapi-kotlin-misk-server";
        artifactVersion = apiVersion;

        typeMapping.put("array", "kotlin.collections.List");

        updateOption(CodegenConstants.API_PACKAGE, apiPackage);
        updateOption(CodegenConstants.MODEL_PACKAGE, modelPackage);
        additionalProperties.put(ROOT_PACKAGE, rootPackage);

        // Add supporting files
        supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
        supportingFiles.add(new SupportingFile("build.gradle.kts.mustache", "", "build.gradle.kts"));
        supportingFiles.add(new SupportingFile("settings.gradle.kts.mustache", "", "settings.gradle.kts"));
    }

    @Override
    public void processOpts() {
        super.processOpts();

        if (additionalProperties.containsKey(ACTION_ANNOTATIONS)) {
            convertPropertyToTypeAndWriteBack(ACTION_ANNOTATIONS,
                    it -> Arrays.asList(it.split(";")), this::setActionAnnotations);
        }
        writePropertyBack(ACTION_ANNOTATIONS, actionAnnotations);

        if (additionalProperties.containsKey(ACTION_IMPORTS)) {
            convertPropertyToTypeAndWriteBack(ACTION_IMPORTS,
                    it -> Arrays.asList(it.split(";")), this::setActionImports);
        }
        writePropertyBack(ACTION_IMPORTS, actionImports);

        if (additionalProperties.containsKey(ACTION_PARENT_CLASS)) {
            setActionParentClass((String) additionalProperties.get(ACTION_PARENT_CLASS));
        }
        writePropertyBack(ACTION_PARENT_CLASS, actionParentClass);

        if (additionalProperties.containsKey(MODULE_CLASS_NAME)) {
            setModuleClassName((String) additionalProperties.get(MODULE_CLASS_NAME));
        }
        writePropertyBack(MODULE_CLASS_NAME, moduleClassName);

        if (additionalProperties.containsKey(ACTION_PATH_PREFIX)) {
            setActionPathPrefix((String) additionalProperties.get(ACTION_PATH_PREFIX));
        }
        writePropertyBack(ACTION_PATH_PREFIX, actionPathPrefix);

        if (additionalProperties.containsKey(ACTION_REQUEST_CONTENT_TYPE)) {
            setActionRequestContentType((String) additionalProperties.get(ACTION_REQUEST_CONTENT_TYPE));
        }
        writePropertyBack(ACTION_REQUEST_CONTENT_TYPE, actionRequestContentType);

        if (additionalProperties.containsKey(ACTION_REQUEST_CONTENT_TYPE_PREFIX)) {
            setActionRequestContentTypePrefix((String) additionalProperties.get(ACTION_REQUEST_CONTENT_TYPE_PREFIX));
        }
        writePropertyBack(ACTION_REQUEST_CONTENT_TYPE_PREFIX, actionRequestContentTypePrefix);

        if (additionalProperties.containsKey(TESTING_MODULE)) {
            setTestingModule((String) additionalProperties.get(TESTING_MODULE));
        }
        writePropertyBack(TESTING_MODULE, testingModule);

        writePropertyBack(TESTING_MODULE_NAME, getTestingModuleName());

        if (additionalProperties.containsKey(USE_BEANVALIDATION)) {
            this.setUseBeanValidation(convertPropertyToBoolean(USE_BEANVALIDATION));
        }
        writePropertyBack(USE_BEANVALIDATION, useBeanValidation);

        if (additionalProperties.containsKey(GENERATE_STUB_IMPL_CLASSES)) {
            setGenerateStubImplClasses(convertPropertyToBoolean(GENERATE_STUB_IMPL_CLASSES));
        }
        writePropertyBack(GENERATE_STUB_IMPL_CLASSES, generateStubImplClasses);

        if (additionalProperties.containsKey(ADD_MODEL_MOSHI_JSON_ANNOTATION)) {
            setAddModelMoshiJsonAnnotation(convertPropertyToBoolean(ADD_MODEL_MOSHI_JSON_ANNOTATION));
        }
        writePropertyBack(ADD_MODEL_MOSHI_JSON_ANNOTATION, addModelMoshiJsonAnnotation);

        applyJakartaPackage();

        String apiModuleFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator);
        String moduleFileName = moduleClassName + ".kt";
        supportingFiles.add(new SupportingFile("miskModule.mustache", apiModuleFolder, moduleFileName));
    }

    @Override
    public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) {
        OperationMap objectMap = objs.getOperations();
        List operations = objectMap.getOperation();

        for (CodegenOperation operation : operations) {

            if (operation.hasConsumes) {
                for (Map consumes : operation.consumes) {
                    consumes.computeIfPresent("mediaType", (__, mediaType) -> mapMediaType(mediaType));
                }
            }

            if (operation.hasProduces) {
                for (Map produces : operation.produces) {
                    produces.computeIfPresent("mediaType", (__, mediaType) -> mapMediaType(mediaType));
                }
            }

            // http method verb conversion (e.g. PUT => Put)
            operation.httpMethod = camelize(operation.httpMethod.toLowerCase(Locale.ROOT));
        }

        return objs;
    }

    @Override
    public void setUseBeanValidation(boolean useBeanValidation) {
        this.useBeanValidation = useBeanValidation;
    }

    public boolean getUseBeanValidation() {
        return this.useBeanValidation;
    }

    private String mapMediaType(String mediaType) {
        return MEDIA_MAPPING.getOrDefault(mediaType, "APPLICATION_OCTETSTREAM /* @todo(unknown) -> " + mediaType + " */ ");
    }

    public String getTestingModuleName() {
        return testingModule.substring(testingModule.lastIndexOf(".")+1);
    }

    private final static Map MEDIA_MAPPING = getMappings();

    private static Map getMappings() {
        // add new values in order
        Map result = new HashMap<>();
        result.put("*/*", "ALL");

        result.put("application/grpc", "APPLICATION_GRPC");
        result.put("application/javascript", "APPLICATION_JAVASCRIPT");
        result.put("application/json", "APPLICATION_JSON");
        result.put("application/jwt", "APPLICATION_JWT");
        result.put("application/octetstream", "APPLICATION_OCTETSTREAM");
        result.put("application/pdf", "APPLICATION_OCTETSTREAM");
        result.put("application/x-protobuf", "APPLICATION_PROTOBUF");
        result.put("application/x-www-form-urlencoded", "APPLICATION_FORM_URLENCODED");
        result.put("application/xml", "APPLICATION_XML");
        result.put("application/zip", "APPLICATION_ZIP");

        result.put("image/gif", "IMAGE_GIF");
        result.put("image/x-icon", "IMAGE_ICO");
        result.put("image/jpeg", "IMAGE_JPEG");
        result.put("image/png", "IMAGE_PNG");
        result.put("image/svg+xml", "IMAGE_SVG");
        result.put("image/tiff", "IMAGE_TIFF");

        result.put("multipart/form-data", "FORM_DATA");

        result.put("text/css", "TEXT_CSS");
        result.put("text/html", "TEXT_HTML");
        result.put("text/plain", "TEXT_PLAIN_UTF8");

        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy