Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.koraframework.common.annotation.Tag;
import io.koraframework.http.client.common.request.HttpClientRequestMapper;
import io.koraframework.http.common.body.HttpBody;
import io.koraframework.http.common.body.HttpBodyOutput;
import io.koraframework.json.common.JsonWriter;
import io.koraframework.json.common.annotation.Json;

Expand All @@ -16,6 +17,11 @@ default HttpClientRequestMapper<byte[]> httpClientRequestByteArrayMapper() {
return (body) -> HttpBody.octetStream(body);
}

@DefaultComponent
default HttpClientRequestMapper<HttpBodyOutput> httpClientRequestBodyOutputMapper() {
return body -> body;
}

@DefaultComponent
default HttpClientRequestMapper<ByteBuffer> httpClientRequestByteBufferMapper() {
return (body) -> HttpBody.octetStream(body);
Expand All @@ -36,7 +42,7 @@ default FormMultipartClientRequestMapper httpClientRequestFormMultipartMapper()
return new FormMultipartClientRequestMapper();
}

@Tag(Json.class)
@Json
@DefaultComponent
default <T> JsonHttpClientRequestMapper<T> httpClientRequestJsonMapper(JsonWriter<T> writer) {
return new JsonHttpClientRequestMapper<>(writer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.koraframework.common.annotation.Tag;
import io.koraframework.http.client.common.response.mapper.JsonHttpClientResponseMapper;
import io.koraframework.http.common.HttpResponseEntity;
import io.koraframework.http.common.body.HttpBodyInput;
import io.koraframework.json.common.JsonReader;
import io.koraframework.json.common.annotation.Json;

Expand Down Expand Up @@ -40,7 +41,12 @@ default HttpClientResponseMapper<ByteBuffer> httpClientResponseByteBufferMapper(
}

@DefaultComponent
default <T> HttpClientResponseMapper<HttpResponseEntity<T>> httpClientResponsEentityResponseMapper(HttpClientResponseMapper<T> mapper) {
default HttpClientResponseMapper<HttpBodyInput> httpClientResponseBodyInputMapper() {
return HttpClientResponse::body;
}

@DefaultComponent
default <T> HttpClientResponseMapper<HttpResponseEntity<T>> httpClientResponseEntityResponseMapper(HttpClientResponseMapper<T> mapper) {
return response -> HttpResponseEntity.of(response.code(), response.headers().toMutable(), mapper.apply(response));
}

Expand All @@ -50,7 +56,7 @@ default <T> HttpClientResponseMapper<HttpResponseEntity<T>> httpClientResponseJs
return response -> HttpResponseEntity.of(response.code(), response.headers().toMutable(), delegate.apply(response));
}

@Tag(Json.class)
@Json
@DefaultComponent
default <T> JsonHttpClientResponseMapper<T> httpClientResponseJsonMapper(JsonReader<T> reader) {
return new JsonHttpClientResponseMapper<>(reader);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.koraframework.http.server.common.request.mapper;

import io.koraframework.common.annotation.DefaultComponent;
import io.koraframework.http.common.body.HttpBodyInput;
import io.koraframework.http.common.form.FormMultipart;
import io.koraframework.http.common.form.FormUrlEncoded;
import io.koraframework.http.server.common.request.HttpServerRequest;
Expand All @@ -19,6 +20,11 @@ default HttpServerRequestMapper<HttpServerRequest> noopHttpServerRequestMapper()
return (r) -> r;
}

@DefaultComponent
default HttpServerRequestMapper<HttpBodyInput> httpBodyInputHttpServerRequestMapper() {
return HttpServerRequest::body;
}

@DefaultComponent
default HttpServerRequestMapper<ByteBuffer> byteBufferHttpServerRequestMapper() {
return (r) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.koraframework.common.annotation.DefaultComponent;
import io.koraframework.http.common.HttpResponseEntity;
import io.koraframework.http.common.body.HttpBody;
import io.koraframework.http.common.body.HttpBodyOutput;
import io.koraframework.http.server.common.response.HttpServerResponse;
import io.koraframework.http.server.common.response.HttpServerResponseMapper;
import io.koraframework.json.common.JsonWriter;
Expand All @@ -27,6 +28,11 @@ default HttpServerResponseMapper<byte[]> byteArrayHttpServerResponseMapper() {
return (request, r) -> HttpServerResponse.of(200, HttpBody.octetStream(r));
}

@DefaultComponent
default HttpServerResponseMapper<HttpBodyOutput> httpBodyOutputHttpServerResponseMapper() {
return (request, r) -> HttpServerResponse.of(200, r);
}

@DefaultComponent
default HttpServerResponseMapper<String> stringHttpServerResponseMapper() {
return (request, r) -> HttpServerResponse.of(200, HttpBody.plaintext(r));
Expand Down
1 change: 1 addition & 0 deletions openapi/openapi-generator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ sourceSets {
addOpenapiDir("petstoreV3_request_parameters")
addOpenapiDir("petstoreV3_responses")
addOpenapiDir("petstoreV3_same_response_model")
addOpenapiDir("petstoreV3_bare_object")
addOpenapiDir("petstoreV3_security_all")
addOpenapiDir("petstoreV3_security_all_auth_arg")
addOpenapiDir("petstoreV3_security_multi")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static class Classes {
public static final ClassName httpBody = ClassName.get("io.koraframework.http.common.body", "HttpBody");
public static final ClassName formMultipart = ClassName.get("io.koraframework.http.common.form", "FormMultipart");
public static final ClassName formPart = formMultipart.nestedClass("FormPart");
public static final ClassName httpBodyInput = ClassName.get("io.koraframework.http.common.body", "HttpBodyInput");
public static final ClassName httpBodyOutput = ClassName.get("io.koraframework.http.common.body", "HttpBodyOutput");

// Client
Expand Down Expand Up @@ -122,6 +123,7 @@ public static class Classes {
public CodegenParams params;
public String apiPackage;
public String modelPackage;
public String outputFolder;
public Map<String, ModelsMap> models;
public Map<String, String> typeMapping;
public Map<String, OperationsMap> operationsByClassName;
Expand All @@ -142,6 +144,9 @@ protected static String toVarName(String s) {
}

public TypeName asType(OperationsMap ctx, CodegenOperation operation, CodegenParameter param) {
if (param.isBodyParam && isBareObject(param)) {
return requestBodyType();
}
if (param.getSchema() != null) {
return asType(param.getSchema());
}
Expand Down Expand Up @@ -185,6 +190,9 @@ public TypeName asType(IJsonSchemaValidationProperties schema) {
if (rs.isFile) {
return ArrayTypeName.of(TypeName.BYTE);
}
if (isBareObject(rs)) {
return responseBodyType();
}
}
if (schema.getIsModel() && schema instanceof CodegenModel c) {
return ClassName.get(modelPackage, c.getClassname());
Expand All @@ -199,6 +207,9 @@ public TypeName asType(IJsonSchemaValidationProperties schema) {
return ParameterizedTypeName.get(ClassName.get(List.class), asType(schema.getItems()).box());
}
if (schema.getIsMap()) {
if (schema.getAdditionalProperties() == null) {
return ClassName.get(Object.class);
}
return ParameterizedTypeName.get(ClassName.get(Map.class), ClassName.get(String.class), asType(schema.getAdditionalProperties()).box());
}
if (schema.getIsModel()) {
Expand Down Expand Up @@ -235,6 +246,9 @@ public TypeName asType(IJsonSchemaValidationProperties schema) {
if (schema instanceof CodegenParameter p && p.isEnumRef) {
return ClassName.get(modelPackage, schema.getDataType());
}
if ("Object".equals(schema.getDataType()) || schema instanceof CodegenProperty p && p.isFreeFormObject) {
return ClassName.get(Object.class);
}
if (schema.getIsLong()) {
return TypeName.LONG;
}
Expand Down Expand Up @@ -287,4 +301,30 @@ public TypeName asType(IJsonSchemaValidationProperties schema) {
}
throw new IllegalArgumentException(schema.toString());
}

protected TypeName requestBodyType() {
if (params.rawBodyMode == CodegenParams.RawBodyMode.BYTES) {
return ArrayTypeName.of(TypeName.BYTE);
}
return params.codegenMode.isClient()
? Classes.httpBodyOutput
: Classes.httpBodyInput;
}

protected TypeName responseBodyType() {
if (params.rawBodyMode == CodegenParams.RawBodyMode.BYTES) {
return ArrayTypeName.of(TypeName.BYTE);
}
return params.codegenMode.isClient()
? Classes.httpBodyInput
: Classes.httpBodyOutput;
}

protected boolean isBareObject(IJsonSchemaValidationProperties schema) {
return "Object".equals(schema.getDataType())
|| schema.getIsMap() && schema.getAdditionalProperties() == null
|| schema instanceof CodegenProperty p && p.isFreeFormObject
|| schema instanceof CodegenParameter cp && cp.isFreeFormObject
|| schema instanceof CodegenResponse r && r.isFreeFormObject;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class CodegenParams {
public static final String IMPLICIT_HEADERS = "implicitHeaders";
public static final String IMPLICIT_HEADERS_REGEX = "implicitHeadersRegex";
public static final String FORCE_INCLUDE_OPTIONAL = "forceIncludeOptional";
public static final String RAW_BODY_MODE = "rawBodyMode";

public CodegenMode codegenMode = CodegenMode.JAVA_CLIENT;
public boolean enableValidation = false;
Expand All @@ -47,6 +48,7 @@ public class CodegenParams {
public boolean implicitHeaders = false;
public @Nullable Pattern implicitHeadersRegex = null;
public boolean forceIncludeOptional = false;
public RawBodyMode rawBodyMode = RawBodyMode.BYTES;

static List<CliOption> cliOptions() {
var cliOptions = new ArrayList<CliOption>();
Expand All @@ -65,6 +67,7 @@ static List<CliOption> cliOptions() {
cliOptions.add(CliOption.newString(PREFIX_PATH, "Path prefix for HTTP Server controllers"));
cliOptions.add(CliOption.newString(DELEGATE_METHOD_BODY_MODE, "Delegate method generation mode"));
cliOptions.add(CliOption.newString(FORCE_INCLUDE_OPTIONAL, "If enabled forces Nullable and NonRequired fields to be included ALWAYS even if null, can't be enabled with enableJsonNullable simultaneously"));
cliOptions.add(CliOption.newString(RAW_BODY_MODE, "Bare object request and response body mode (one of BYTES, RAW)"));
return cliOptions;
}

Expand Down Expand Up @@ -143,6 +146,18 @@ static CodegenParams parse(Map<String, Object> additionalProperties) {
if (additionalProperties.containsKey(FORCE_INCLUDE_OPTIONAL)) {
params.forceIncludeOptional = Boolean.parseBoolean(additionalProperties.get(FORCE_INCLUDE_OPTIONAL).toString());
}
if (additionalProperties.containsKey(RAW_BODY_MODE)) {
params.rawBodyMode = RawBodyMode.of(additionalProperties.get(RAW_BODY_MODE).toString());
}
return params;
}

public enum RawBodyMode {
BYTES,
RAW;

public static RawBodyMode of(String value) {
return RawBodyMode.valueOf(value.toUpperCase(Locale.ROOT));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
public class KoraCodegen extends DefaultCodegen {

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

public record TagClient(@Nullable String httpClientTag, @Nullable String telemetryTag) {}

public record Interceptor(@Nullable String type, @Nullable Object tag) {}
Expand Down Expand Up @@ -1879,6 +1878,7 @@ <C, T extends AbstractGenerator<C, JavaFile>> Mustache.Lambda javaGen(T gen) {
return (frag, out) -> {
gen.apiPackage = apiPackage;
gen.modelPackage = modelPackage;
gen.outputFolder = outputFolder;
gen.params = params;
gen.models = models;
gen.operationsByClassName = operationsByClassName;
Expand All @@ -1893,6 +1893,7 @@ <C, T extends AbstractGenerator<C, FileSpec>> Mustache.Lambda kotlinGen(T gen) {
return (frag, out) -> {
gen.apiPackage = apiPackage;
gen.modelPackage = modelPackage;
gen.outputFolder = outputFolder;
gen.params = params;
gen.models = models;
gen.operationsByClassName = operationsByClassName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ protected ParameterSpec buildParameter(OperationsMap ctx, CodegenOperation opera
.addMember("value", "$S", param.baseName)
.build());
}
if (param.isBodyParam && KoraCodegen.isContentJson(param)) {
if (param.isBodyParam && KoraCodegen.isContentJson(param) && !isBareObject(param)) {
b.addAnnotation(jsonAnnotation());
}
if (params.codegenMode.isServer() && params.enableValidation) {
Expand Down Expand Up @@ -202,12 +202,13 @@ protected CodeBlock buildMethodJavadoc(OperationsMap ctx, CodegenOperation opera
var b = CodeBlock.builder();
b.add(operation.httpMethod + " " + operation.path);
if (operation.summary != null) {
b.add(": " + operation.summary);
b.add(" : " + operation.summary);
}
b.add("\n");
if (operation.notes != null) {
b.add(operation.notes).add("\n");
}
b.add("\n");
for (var param : operation.allParams) {
if (!param.isFormParam) {
b.add("@param ").add(param.paramName).add(" ");
Expand All @@ -228,6 +229,20 @@ protected CodeBlock buildMethodJavadoc(OperationsMap ctx, CodegenOperation opera
b.add("\n");
}
}
if (!operation.responses.isEmpty()) {
b.add("@return ");
for (var i = 0; i < operation.responses.size(); i++) {
if (i > 0) {
b.add(" or ");
}
var response = operation.responses.get(i);
b.add(Objects.requireNonNullElse(response.message, ""));
b.add(" (status code ");
b.add(response.isDefault ? "default" : response.code);
b.add(")");
}
b.add("\n");
}
if (operation.isDeprecated) {
b.add("@deprecated\n");
}
Expand Down
Loading
Loading