Skip to content

Kotlin Misk Add Extra Parameters #21271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 16, 2025
Merged
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
9 changes: 8 additions & 1 deletion bin/configs/kotlin-misk-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ outputDir: samples/server/petstore/kotlin-misk-config
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-misk
validateSpec: false
useBeanValidation: true
additionalProperties:
hideGenerationTimestamp: "true"
moduleClassName: "PetStoreModule"
generateStubImplClasses: true
addModelMoshiJsonAnnotation: true
actionPathPrefix : "samplePrefix"
actionPathPrefix: "samplePrefix"
actionAnnotations: "@LogRequestResponse(bodySampling = 1.0, errorBodySampling = 2.0);@Suppress(\"unused\")"
actionImports: "misk.web.actions.WebAction;misk.web.interceptors.LogRequestResponse"
actionParentClass: "WebAction"
actionRequestContentType: "@RequestContentType"
actionRequestContentTypePrefix: "MediaTypes"
testingModule: "misk.web.MiskWebModule"
6 changes: 6 additions & 0 deletions docs/generators/kotlin-misk.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ These options may be applied as additional-properties (cli) or configOptions (pl

| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|actionAnnotations|String Annotations for Actions separated by a semicolon(;)| |@LogRequestResponse(bodySampling = 1.0, errorBodySampling = 1.0)|
|actionImports|String Imports for Actions separated by a semicolon(;)| |misk.web.actions.WebAction;misk.web.interceptors.LogRequestResponse|
|actionParentClass|Parent Class for Action| |WebAction|
|actionPathPrefix|Prefix for action path| ||
|actionRequestContentType|Request ContentType for Action| |@RequestContentType|
|actionRequestContentTypePrefix|Request ContentType Prefix for Action| |MediaTypes|
|addModelMoshiJsonAnnotation|Add a Moshi JSON adapter annotation to all model classes| |true|
|additionalModelTypeAnnotations|Additional annotations for model type(class level annotations). List separated by semicolon(;) or new line (Linux or Windows)| |null|
|apiSuffix|suffix for api classes| |Api|
Expand All @@ -35,6 +40,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |null|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |null|
|sourceFolder|source folder for generated code| |src/main/kotlin|
|testingModule|Testing module class| |misk.testing.MiskTestModule|
|useBeanValidation|Use BeanValidation API annotations to validate data types| |true|

## IMPORT MAPPING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
Expand All @@ -55,6 +56,13 @@ public class KotlinMiskServerCodegen extends AbstractKotlinCodegen implements Be
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;

Expand All @@ -69,6 +77,15 @@ public class KotlinMiskServerCodegen extends AbstractKotlinCodegen implements Be

@Setter protected String moduleClassName = "OpenApiModule";
@Setter protected String actionPathPrefix = "";
@Setter protected List<String> actionAnnotations =
List.of("@LogRequestResponse(bodySampling = 1.0, errorBodySampling = 1.0)");
@Setter protected List<String> 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() {
Expand Down Expand Up @@ -122,6 +139,12 @@ public KotlinMiskServerCodegen() {

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");
Expand Down Expand Up @@ -163,6 +186,23 @@ public KotlinMiskServerCodegen() {
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));
}
Expand All @@ -173,6 +213,23 @@ public void processOpts() {
}
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));
}
Expand All @@ -187,6 +244,7 @@ public void processOpts() {
setAddModelMoshiJsonAnnotation(convertPropertyToBoolean(ADD_MODEL_MOSHI_JSON_ANNOTATION));
}
writePropertyBack(ADD_MODEL_MOSHI_JSON_ANNOTATION, addModelMoshiJsonAnnotation);

applyJakartaPackage();

String apiModuleFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator);
Expand Down Expand Up @@ -230,39 +288,43 @@ public boolean getUseBeanValidation() {
}

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

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

private final static Map<String, String> MEDIA_MAPPING = getMappings();

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

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

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

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

result.put("text/css", "MediaTypes.TEXT_CSS");
result.put("text/html", "MediaTypes.TEXT_HTML");
result.put("text/plain", "MediaTypes.TEXT_PLAIN_UTF8");
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {{javaxPackage}}.validation.constraints.NotNull
import {{javaxPackage}}.validation.constraints.Pattern
import {{javaxPackage}}.validation.constraints.Size
{{/useBeanValidation}}
{{#actionImports}}
import {{{.}}}
{{/actionImports}}
import misk.web.Delete
import misk.web.Description
import misk.web.Get
Expand All @@ -26,10 +29,9 @@ import misk.web.RequestBody
import misk.web.RequestContentType
import misk.web.RequestHeader
import misk.web.ResponseContentType
import misk.web.actions.WebAction
import misk.web.interceptors.LogRequestResponse
import misk.web.mediatype.MediaTypes
{{#imports}}import {{import}}
{{#imports}}
import {{import}}
{{/imports}}

/**
Expand All @@ -38,14 +40,16 @@ import misk.web.mediatype.MediaTypes
{{#operations}}
@Singleton
class {{classname}}Action @Inject constructor(
) : WebAction {
) : {{actionParentClass}} {
{{#operation}}

@{{httpMethod}}("{{actionPathPrefix}}{{path}}")
@{{httpMethod}}("{{{actionPathPrefix}}}{{path}}")
@Description("{{{summary}}}"){{#hasConsumes}}
@RequestContentType({{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}){{/hasConsumes}}{{#hasProduces}}
@ResponseContentType({{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}){{/hasProduces}}
@LogRequestResponse(bodySampling = 1.0, errorBodySampling = 1.0)
{{{actionRequestContentType}}}({{#consumes}}{{{actionRequestContentTypePrefix}}}.{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}){{/hasConsumes}}{{#hasProduces}}
@ResponseContentType({{#produces}}MediaTypes.{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}){{/hasProduces}}
{{#actionAnnotations}}
{{{.}}}
{{/actionAnnotations}}
fun {{operationId}}({{#allParams}}
{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>cookieParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}}: {{{returnType}}}{{/returnType}} {
TODO()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import misk.web.PathParam
import misk.web.QueryParam
import misk.web.RequestBody
import misk.web.RequestHeader
{{#imports}}import {{import}}
{{#imports}}
import {{import}}
{{/imports}}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import misk.web.PathParam
import misk.web.QueryParam
import misk.web.RequestBody
import misk.web.RequestHeader
{{#imports}}import {{import}}
{{#imports}}
import {{import}}
{{/imports}}

{{#operations}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
package {{package}}

import {{{testingModule}}}
import {{javaxPackage}}.inject.Inject
import misk.testing.MiskTest
import misk.testing.MiskTestModule
import org.junit.jupiter.api.Test

import misk.web.HttpCall
import misk.web.PathParam
import misk.web.QueryParam
import misk.web.RequestBody
import misk.web.RequestHeader

{{#imports}}import {{import}}
{{#imports}}
import {{import}}
{{/imports}}

@MiskTest(startService = true)
internal class {{classname}}Test {

@Suppress("unused")
@MiskTestModule
private val module = {{{testingModuleName}}}()

@Inject private lateinit var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}: {{classname}}Action
{{#operations}}
{{#operation}}

{{#operations}}
{{#operation}}
/**
* To test {{classname}}Action.{{operationId}}
*/
@Test
fun `should handle {{operationId}}`() {
{{#allParams}}
{{#allParams}}
val {{{paramName}}} = TODO()
{{/allParams}}
{{/allParams}}
val response{{#returnType}}: {{{returnType}}}{{/returnType}} = {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
}

{{/operation}}
{{/operations}}
{{/operation}}
{{/operations}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ protected void verifyOptions() {
verify(codegen).setUseBeanValidation(Boolean.valueOf(KotlinMiskServerCodegenOptionsProvider.USE_BEAN_VALIDATION));
verify(codegen).setModuleClassName(KotlinMiskServerCodegenOptionsProvider.MODULE_CLASS_NAME);
verify(codegen).setActionPathPrefix(KotlinMiskServerCodegenOptionsProvider.ACTION_PATH_PREFIX);
verify(codegen).setActionImports(List.of("a.x","b.y"));
verify(codegen).setActionAnnotations(List.of("@c()","@d()"));
verify(codegen).setActionParentClass(KotlinMiskServerCodegenOptionsProvider.ACTION_PARENT_CLASS);
verify(codegen).setActionRequestContentType(KotlinMiskServerCodegenOptionsProvider.ACTION_REQUEST_CONTENT_TYPE);
verify(codegen).setActionRequestContentTypePrefix(KotlinMiskServerCodegenOptionsProvider.ACTION_REQUEST_CONTENT_TYPE_PREFIX);
verify(codegen).setGenerateStubImplClasses(Boolean.valueOf(KotlinMiskServerCodegenOptionsProvider.GENERATE_STUB_IMPL_CLASSES));
verify(codegen).setAddModelMoshiJsonAnnotation(Boolean.valueOf(KotlinMiskServerCodegenOptionsProvider.ADD_MODEL_MOSHI_JSON_ANNOTATION));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ public class KotlinMiskServerCodegenOptionsProvider implements OptionsProvider {
public static final String GENERATE_STUB_IMPL_CLASSES = "false";
public static final String ADD_MODEL_MOSHI_JSON_ANNOTATION = "true";
public static final String MODULE_CLASS_NAME = "OpenApiModule";
public static final String ACTION_PATH_PREFIX = "samplePrefix";
public static final String ACTION_PATH_PREFIX = "samplePrefix<";
public static final String ACTION_IMPORTS = "a.x;b.y";
public static final String ACTION_ANNOTATIONS = "@c();@d()";
public static final String ACTION_PARENT_CLASS = "class<";
public static final String ACTION_REQUEST_CONTENT_TYPE = "contentType<";
public static final String ACTION_REQUEST_CONTENT_TYPE_PREFIX = "contentTypePrefix<";
public static final String TESTING_MODULE = "testingModule";

@Override
public String getLanguage() {
Expand Down Expand Up @@ -55,13 +61,19 @@ public Map<String, String> createOptions() {
.put(KotlinMiskServerCodegen.MODULE_CLASS_NAME, MODULE_CLASS_NAME)
.put(BeanValidationFeatures.USE_BEANVALIDATION, USE_BEAN_VALIDATION)
.put(KotlinMiskServerCodegen.ACTION_PATH_PREFIX, ACTION_PATH_PREFIX)
.put(KotlinMiskServerCodegen.ACTION_IMPORTS, ACTION_IMPORTS)
.put(KotlinMiskServerCodegen.ACTION_ANNOTATIONS, ACTION_ANNOTATIONS)
.put(KotlinMiskServerCodegen.ACTION_PARENT_CLASS, ACTION_PARENT_CLASS)
.put(KotlinMiskServerCodegen.ACTION_REQUEST_CONTENT_TYPE, ACTION_REQUEST_CONTENT_TYPE)
.put(KotlinMiskServerCodegen.ACTION_REQUEST_CONTENT_TYPE_PREFIX, ACTION_REQUEST_CONTENT_TYPE_PREFIX)
.put(KotlinMiskServerCodegen.ADD_MODEL_MOSHI_JSON_ANNOTATION, ADD_MODEL_MOSHI_JSON_ANNOTATION)
.put(KotlinMiskServerCodegen.GENERATE_STUB_IMPL_CLASSES, GENERATE_STUB_IMPL_CLASSES)
.put(KotlinMiskServerCodegen.TESTING_MODULE, TESTING_MODULE)
.build();
}

@Override
public boolean isServer() {
return true;
}
}
}
Loading
Loading