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

ru.curs.hurdygurdy.JavaAPIExtractor Maven / Gradle / Ivy

There is a newer version: 2.01
Show newest version
package ru.curs.hurdygurdy;

import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.RequestBody;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;

import javax.lang.model.element.Modifier;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Optional;

public class JavaAPIExtractor extends APIExtractor {

    public JavaAPIExtractor(TypeDefiner typeDefiner,
                            boolean generateResponseParameter,
                            boolean generateApiInterface) {
        super(typeDefiner, generateResponseParameter, generateApiInterface,
                TypeSpec::interfaceBuilder,
                TypeSpec.Builder::build);
    }


    @Override
    void buildMethod(OpenAPI openAPI, TypeSpec.Builder classBuilder,
                     Map.Entry stringPathItemEntry,
                     Map.Entry operationEntry,
                     boolean generateResponseParameter) {
        MethodSpec.Builder methodBuilder = MethodSpec
                .methodBuilder(CaseUtils.snakeToCamel(operationEntry.getValue().getOperationId()))
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
        methodBuilder.addAnnotation(getControllerMethodAnnotationSpec(operationEntry, stringPathItemEntry.getKey()));
        //we are deriving the returning type from the schema of the successful result
        methodBuilder.returns(determineReturnJavaType(operationEntry.getValue(), openAPI, classBuilder));
        Optional.ofNullable(operationEntry.getValue().getRequestBody()).map(RequestBody::getContent)
                .map(c -> getContentType(c, openAPI, classBuilder)).ifPresent(typeName ->
                        methodBuilder.addParameter(ParameterSpec.builder(
                                        typeName,
                                        "request")
                                .addAnnotation(org.springframework.web.bind.annotation.RequestBody.class).build())
                );

        getParameterStream(stringPathItemEntry.getValue(), operationEntry.getValue())
                .filter(parameter -> "path".equalsIgnoreCase(parameter.getIn()))
                .forEach(parameter -> methodBuilder.addParameter(ParameterSpec.builder(
                                safeUnbox(typeDefiner.defineJavaType(parameter.getSchema(), openAPI, classBuilder)),
                                CaseUtils.snakeToCamel(parameter.getName()))
                        .addAnnotation(
                                AnnotationSpec.builder(PathVariable.class)
                                        .addMember("name", "$S", parameter.getName()).build()
                        )
                        .build()));
        getParameterStream(stringPathItemEntry.getValue(), operationEntry.getValue())
                .filter(parameter -> "query".equalsIgnoreCase(parameter.getIn()))
                .forEach(parameter -> {
                            AnnotationSpec.Builder builder = AnnotationSpec.builder(
                                            RequestParam.class
                                    ).addMember("required", "$L", parameter.getRequired())
                                    .addMember("name", "$S", parameter.getName());

                            Optional.ofNullable(parameter.getSchema())
                                    .map(Schema::getDefault)
                                    .ifPresent(
                                            d -> builder.addMember("defaultValue", "$S", d.toString()));

                            AnnotationSpec annotationSpec = builder.build();
                            methodBuilder.addParameter(ParameterSpec.builder(
                                            safeBox(typeDefiner.defineJavaType(parameter.getSchema(), openAPI,
                                                    classBuilder)),
                                            CaseUtils.snakeToCamel(parameter.getName()))
                                    .addAnnotation(annotationSpec).build());
                        }
                );
        getParameterStream(stringPathItemEntry.getValue(), operationEntry.getValue())
                .filter(parameter -> "header".equalsIgnoreCase(parameter.getIn()))
                .forEach(parameter -> methodBuilder.addParameter(ParameterSpec.builder(
                                safeBox(typeDefiner.defineJavaType(parameter.getSchema(), openAPI, classBuilder)),
                                CaseUtils.kebabToCamel(parameter.getName()))
                        .addAnnotation(
                                AnnotationSpec.builder(
                                                RequestHeader.class
                                        ).addMember("required", "$L", parameter.getRequired())
                                        .addMember("name", "$S", parameter.getName()).build()
                        ).build()));
        if (generateResponseParameter) {
            methodBuilder.addParameter(ParameterSpec.builder(
                    HttpServletResponse.class,
                    "response").build());
        }
        classBuilder.addMethod(methodBuilder.build());
    }

    private static TypeName safeBox(TypeName name) {
        return name.isPrimitive() ? name.box() : name;
    }

    private static TypeName safeUnbox(TypeName name) {
        return name.isBoxedPrimitive() ? name.unbox() : name;
    }

    private TypeName determineReturnJavaType(Operation operation, OpenAPI openAPI, TypeSpec.Builder parent) {
        return getSuccessfulReply(operation).map(c -> getContentType(c, openAPI, parent)).orElse(TypeName.VOID);
    }

    private TypeName getContentType(Content content, OpenAPI openAPI, TypeSpec.Builder parent) {
        return Optional.ofNullable(content)
                .flatMap(APIExtractor::getMediaType)
                .map(Map.Entry::getValue)
                .map(MediaType::getSchema)
                .map(s -> typeDefiner.defineJavaType(s, openAPI, parent))
                .map(JavaAPIExtractor::safeUnbox)
                .orElse(TypeName.VOID);
    }

    private AnnotationSpec getControllerMethodAnnotationSpec(Map.Entry operationEntry,
                                                             String path) {
        Class annotationClass = null;
        switch (operationEntry.getKey()) {
            case GET:
                annotationClass = GetMapping.class;
                break;
            case POST:
                annotationClass = PostMapping.class;
                break;
            case PUT:
                annotationClass = PutMapping.class;
                break;
            case PATCH:
                annotationClass = PatchMapping.class;
                break;
            case DELETE:
                annotationClass = DeleteMapping.class;
                break;
        }
        if (annotationClass != null) {
            AnnotationSpec.Builder builder = AnnotationSpec.builder(annotationClass)
                    .addMember("value", "$S", path);
            getSuccessfulReply(operationEntry.getValue())
                    .flatMap(APIExtractor::getMediaType)
                    .map(Map.Entry::getKey)
                    .ifPresent(mt -> builder.addMember("produces", "$S", mt));
            return builder.build();
        } else return null;

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy