Skip to content

Commit

Permalink
Add support implicitHeaders and implicitHeadersRegex (#1730)
Browse files Browse the repository at this point in the history
Fixed #1729
  • Loading branch information
altro3 committed Sep 3, 2024
1 parent 3645abf commit 25bb2a6
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@
public @interface OpenAPIGroup {

/**
* @return The names of the OpenAPi groups.
* @return The names of the OpenAPI groups.
*/
@AliasFor(member = "names")
String[] value() default {};

/**
* @return The names of the OpenAPi groups.
* @return The names of the OpenAPI groups.
*/
@AliasFor(member = "value")
String[] names() default {};

/**
* @return The names of the OpenAPi groups to exclude endpoints from.
* @return The names of the OpenAPI groups to exclude endpoints from.
*/
String[] exclude() default {};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@
public @interface OpenAPIGroupInfo {

/**
* @return The names of the OpenAPi groups.
* @return The names of the OpenAPI groups.
*/
@AliasFor(member = "names")
String[] value() default {};

/**
* @return The names of the OpenAPi groups.
* @return The names of the OpenAPI groups.
*/
@AliasFor(member = "value")
String[] names() default {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,10 @@ public CodegenModel fromModel(String name, Schema model) {
return codegenModel;
}

private boolean shouldBeImplicitHeader(CodegenParameter parameter) {
return StringUtils.isNotBlank(implicitHeadersRegex) && parameter.baseName.matches(implicitHeadersRegex);
}

@Override
public String toEnumValue(String value, String datatype) {
if ("Integer".equals(datatype) || "Double".equals(datatype)) {
Expand Down Expand Up @@ -1279,19 +1283,27 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
op.imports.add(op.returnType);
}

op.vendorExtensions.put("originalParams", new ArrayList<>(op.allParams));
var paramsWithoutImplicitHeaders = new ArrayList<CodegenParameter>();
var swaggerParams = new ArrayList<CodegenParameter>();
var hasMultipleParams = false;
var notBodyParamsSize = 0;
for (var param : op.allParams) {
if (param.isBodyParam) {
if (!param.isHeaderParam || (!implicitHeaders && !shouldBeImplicitHeader(param))) {
paramsWithoutImplicitHeaders.add(param);
}
if (param.isBodyParam || param.isFormParam) {
continue;
}
swaggerParams.add(param);
notBodyParamsSize++;
if (notBodyParamsSize > 1) {
hasMultipleParams = true;
break;
}
}
op.vendorExtensions.put("swaggerParams", swaggerParams);
op.vendorExtensions.put("originalParams", paramsWithoutImplicitHeaders);
op.vendorExtensions.put("hasNotBodyParam", notBodyParamsSize > 0);
op.vendorExtensions.put("hasMultipleParams", hasMultipleParams);
for (var param : op.allParams) {
param.vendorExtensions.put("hasNotBodyParam", notBodyParamsSize > 0);
param.vendorExtensions.put("hasMultipleParams", hasMultipleParams);
Expand Down Expand Up @@ -1889,6 +1901,13 @@ public boolean getUseInlineModelResolver() {
return false;
}

public void setGenerateSwaggerAnnotations(boolean generateSwaggerAnnotations) {
this.generateSwaggerAnnotations = Boolean.toString(generateSwaggerAnnotations);
if (generateSwaggerAnnotations) {
additionalProperties.put("generateSwagger2Annotations", true);
}
}

@Override
public void postProcess() {
// disable output donation suggestion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ public abstract class AbstractMicronautKotlinCodegen<T extends GeneratorOptionsB
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_TRUE = "true";
public static final String OPT_GENERATE_SWAGGER_ANNOTATIONS_FALSE = "false";
public static final String OPT_GENERATE_OPERATION_ONLY_FOR_FIRST_TAG = "generateOperationOnlyForFirstTag";
public static final String OPT_IMPLICIT_HEADERS = "implicitHeaders";
public static final String OPT_IMPLICIT_HEADERS_REGEX = "implicitHeadersRegex";
public static final String OPT_KSP = "ksp";
public static final String CONTENT_TYPE_APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
public static final String CONTENT_TYPE_APPLICATION_JSON = "application/json";
Expand All @@ -144,6 +146,8 @@ public abstract class AbstractMicronautKotlinCodegen<T extends GeneratorOptionsB
protected boolean generateHttpResponseAlways;
protected boolean generateHttpResponseWhereRequired = true;
protected boolean ksp;
protected boolean implicitHeaders = false;
protected String implicitHeadersRegex;
protected String appName;
protected String generateSwaggerAnnotations;
protected boolean generateOperationOnlyForFirstTag;
Expand Down Expand Up @@ -267,6 +271,8 @@ protected AbstractMicronautKotlinCodegen() {
cliOptions.add(CliOption.newBoolean(OPT_VISITABLE, "Generate visitor for subtypes with a discriminator", visitable));
cliOptions.add(CliOption.newBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR, "Allow only to create models with all the required properties provided in constructor", requiredPropertiesInConstructor));
cliOptions.add(CliOption.newBoolean(OPT_REACTIVE, "Make the responses use Reactor Mono as wrapper", reactive));
cliOptions.add(CliOption.newBoolean(OPT_IMPLICIT_HEADERS, "Skip header parameters in the generated API methods using @ApiImplicitParams annotation.", implicitHeaders));
cliOptions.add(CliOption.newString(OPT_IMPLICIT_HEADERS_REGEX, "Skip header parameters that matches given regex in the generated API methods using @ApiImplicitParams annotation. Note: this parameter is ignored when implicitHeaders=true"));
cliOptions.add(CliOption.newBoolean(OPT_GENERATE_HTTP_RESPONSE_ALWAYS, "Always wrap the operations response in HttpResponse object", generateHttpResponseAlways));
cliOptions.add(CliOption.newBoolean(OPT_GENERATE_HTTP_RESPONSE_WHERE_REQUIRED, "Wrap the operations response in HttpResponse object where non-200 HTTP status codes or additional headers are defined", generateHttpResponseWhereRequired));
cliOptions.add(CliOption.newBoolean(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC, isHideGenerationTimestamp()));
Expand Down Expand Up @@ -377,6 +383,14 @@ public void setReactive(boolean reactive) {
this.reactive = reactive;
}

public void setImplicitHeaders(boolean implicitHeaders) {
this.implicitHeaders = implicitHeaders;
}

public void setImplicitHeadersRegex(String implicitHeadersRegex) {
this.implicitHeadersRegex = implicitHeadersRegex;
}

public void setTestTool(String testTool) {
this.testTool = testTool;
}
Expand Down Expand Up @@ -516,6 +530,9 @@ public void processOpts() {
additionalProperties.put("isTestJunit", true);
}

convertPropertyToBooleanAndWriteBack(OPT_IMPLICIT_HEADERS, this::setImplicitHeaders);
convertPropertyToStringAndWriteBack(OPT_IMPLICIT_HEADERS_REGEX, this::setImplicitHeadersRegex);

maybeSetSwagger();
if (OPT_GENERATE_SWAGGER_ANNOTATIONS_SWAGGER_2.equals(generateSwaggerAnnotations)) {
additionalProperties.put("generateSwagger2Annotations", true);
Expand Down Expand Up @@ -850,6 +867,10 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
List<CodegenOperation> operationList = operations.getOperation();

for (CodegenOperation op : operationList) {

handleImplicitHeaders(op);
handleConstantParams(op);

// Set whether body is supported in request
op.vendorExtensions.put("methodAllowsBody", op.httpMethod.equals("PUT")
|| op.httpMethod.equals("POST")
Expand Down Expand Up @@ -947,6 +968,34 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
return objs;
}

/**
* This method removes all implicit header parameters from the list of parameters
*
* @param operation - operation to be processed
*/
protected void handleImplicitHeaders(CodegenOperation operation) {
if (operation.allParams.isEmpty()) {
return;
}
final ArrayList<CodegenParameter> copy = new ArrayList<>(operation.allParams);
operation.allParams.clear();

for (CodegenParameter p : copy) {
if (p.isHeaderParam && (implicitHeaders || shouldBeImplicitHeader(p))) {
operation.implicitHeadersParams.add(p);
operation.headerParams.removeIf(header -> header.baseName.equals(p.baseName));
once(log).info("Update operation [{}]. Remove header [{}] because it's marked to be implicit", operation.operationId, p.baseName);
} else {
operation.allParams.add(p);
}
}
operation.hasParams = !operation.allParams.isEmpty();
}

private boolean shouldBeImplicitHeader(CodegenParameter parameter) {
return StringUtils.isNotBlank(implicitHeadersRegex) && parameter.baseName.matches(implicitHeadersRegex);
}

@Override
public String toEnumValue(String value, String datatype) {
if ("Integer".equals(datatype)
Expand Down Expand Up @@ -1168,19 +1217,28 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
op.imports.add(op.returnType);
}

op.vendorExtensions.put("originalParams", new ArrayList<>(op.allParams));
var paramsWithoutImplicitHeaders = new ArrayList<CodegenParameter>();
var swaggerParams = new ArrayList<CodegenParameter>();
var hasMultipleParams = false;
var notBodyParamsSize = 0;
for (var param : op.allParams) {
if (param.isBodyParam) {
if (!param.isHeaderParam || (!implicitHeaders && !shouldBeImplicitHeader(param))) {
param.vendorExtensions.computeIfAbsent("realName", k -> param.paramName);
paramsWithoutImplicitHeaders.add(param);
}
if (param.isBodyParam || param.isFormParam) {
continue;
}
swaggerParams.add(param);
notBodyParamsSize++;
if (notBodyParamsSize > 1) {
hasMultipleParams = true;
break;
}
}
op.vendorExtensions.put("swaggerParams", swaggerParams);
op.vendorExtensions.put("originalParams", paramsWithoutImplicitHeaders);
op.vendorExtensions.put("hasNotBodyParam", notBodyParamsSize > 0);
op.vendorExtensions.put("hasMultipleParams", hasMultipleParams);
for (var param : op.allParams) {
param.vendorExtensions.put("hasNotBodyParam", notBodyParamsSize > 0);
param.vendorExtensions.put("hasMultipleParams", hasMultipleParams);
Expand Down Expand Up @@ -2001,6 +2059,13 @@ public boolean getUseInlineModelResolver() {
return false;
}

public void setGenerateSwaggerAnnotations(boolean generateSwaggerAnnotations) {
this.generateSwaggerAnnotations = Boolean.toString(generateSwaggerAnnotations);
if (generateSwaggerAnnotations) {
additionalProperties.put("generateSwagger2Annotations", true);
}
}

@Override
public void postProcess() {
// disable output donation suggestion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,21 @@ private void configureOptions() {
if (options.modelNameSuffix != null && !options.modelNameSuffix.isBlank()) {
javaCodeGen.setModelNameSuffix(options.modelNameSuffix);
}
javaCodeGen.setImplicitHeaders(options.implicitHeaders);
if (options.implicitHeadersRegex != null && !options.implicitHeadersRegex.isBlank()) {
javaCodeGen.setImplicitHeadersRegex(options.implicitHeadersRegex);
}

javaCodeGen.setUseOneOfInterfaces(options.useOneOfInterfaces());
javaCodeGen.setUseOneOfInterfaces(options.useOneOfInterfaces);
javaCodeGen.setReactive(options.reactive);
javaCodeGen.setGenerateHttpResponseAlways(options.generateHttpResponseAlways);
javaCodeGen.setGenerateHttpResponseWhereRequired(options.generateHttpResponseWhereRequired);
javaCodeGen.setUseOptional(options.optional);
javaCodeGen.setUseBeanValidation(options.beanValidation);
javaCodeGen.setTestTool(options.testFramework.value);
javaCodeGen.setSerializationLibrary(options.serializationLibraryKind().name());
javaCodeGen.setDateTimeLibrary(options.dateTimeFormat().name());
javaCodeGen.setSerializationLibrary(options.serializationLibraryKind.name());
javaCodeGen.setGenerateSwaggerAnnotations(options.generateSwaggerAnnotations);
javaCodeGen.setDateTimeLibrary(options.dateTimeFormat.name());
configureJavaServerOptions();
configureJavaClientOptions();
} else if (options.lang == GeneratorLanguage.KOTLIN && codeGenerator instanceof AbstractMicronautKotlinCodegen<?> kotlinCodeGen) {
Expand Down Expand Up @@ -245,14 +250,19 @@ private void configureOptions() {
if (options.modelNameSuffix != null && !options.modelNameSuffix.isBlank()) {
kotlinCodeGen.setModelNameSuffix(options.modelNameSuffix);
}
kotlinCodeGen.setImplicitHeaders(options.implicitHeaders);
if (options.implicitHeadersRegex != null && !options.implicitHeadersRegex.isBlank()) {
kotlinCodeGen.setImplicitHeadersRegex(options.implicitHeadersRegex);
}
kotlinCodeGen.setUseOneOfInterfaces(options.useOneOfInterfaces);
kotlinCodeGen.setReactive(options.reactive);
kotlinCodeGen.setGenerateHttpResponseAlways(options.generateHttpResponseAlways);
kotlinCodeGen.setGenerateHttpResponseWhereRequired(options.generateHttpResponseWhereRequired);
kotlinCodeGen.setGenerateSwaggerAnnotations(options.generateSwaggerAnnotations);
kotlinCodeGen.setUseBeanValidation(options.beanValidation);
kotlinCodeGen.setTestTool(options.testFramework.value);
kotlinCodeGen.setSerializationLibrary(options.serializationLibraryKind().name());
kotlinCodeGen.setDateTimeLibrary(options.dateTimeFormat().name());
kotlinCodeGen.setSerializationLibrary(options.serializationLibraryKind.name());
kotlinCodeGen.setDateTimeLibrary(options.dateTimeFormat.name());
configureKotlinServerOptions();
configureKotlinClientOptions();
}
Expand Down Expand Up @@ -511,11 +521,15 @@ private static class DefaultOptionsBuilder implements MicronautCodeGeneratorOpti
private String modelNamePrefix;
private String modelNameSuffix;

private boolean implicitHeaders;
private String implicitHeadersRegex;

private boolean optional;
private boolean reactive = true;
private boolean useOneOfInterfaces = true;
private boolean generateHttpResponseAlways;
private boolean generateHttpResponseWhereRequired = true;
private boolean generateSwaggerAnnotations;
private TestFramework testFramework = TestFramework.JUNIT5;
private SerializationLibraryKind serializationLibraryKind = SerializationLibraryKind.MICRONAUT_SERDE_JACKSON;
private DateTimeFormat dateTimeFormat = DateTimeFormat.ZONED_DATETIME;
Expand Down Expand Up @@ -641,6 +655,18 @@ public MicronautCodeGeneratorOptionsBuilder withModelNameSuffix(String modelName
return this;
}

@Override
public MicronautCodeGeneratorOptionsBuilder withImplicitHeaders(boolean implicitHeaders) {
this.implicitHeaders = implicitHeaders;
return this;
}

@Override
public MicronautCodeGeneratorOptionsBuilder withImplicitHeadersRegex(String implicitHeadersRegex) {
this.implicitHeadersRegex = implicitHeadersRegex;
return this;
}

@Override
public MicronautCodeGeneratorOptionsBuilder withReactive(boolean reactive) {
this.reactive = reactive;
Expand All @@ -659,6 +685,12 @@ public MicronautCodeGeneratorOptionsBuilder withGenerateHttpResponseWhereRequire
return this;
}

@Override
public MicronautCodeGeneratorOptionsBuilder withGenerateSwaggerAnnotations(boolean generateSwaggerAnnotations) {
this.generateSwaggerAnnotations = generateSwaggerAnnotations;
return this;
}

@Override
public MicronautCodeGeneratorOptionsBuilder withBeanValidation(boolean beanValidation) {
this.beanValidation = beanValidation;
Expand Down Expand Up @@ -719,12 +751,16 @@ private Options build() {
modelNamePrefix,
modelNameSuffix,

implicitHeaders,
implicitHeadersRegex,

beanValidation,
optional,
reactive,
useOneOfInterfaces,
generateHttpResponseAlways,
generateHttpResponseWhereRequired,
generateSwaggerAnnotations,
testFramework,
serializationLibraryKind,
dateTimeFormat);
Expand Down Expand Up @@ -771,12 +807,16 @@ private record Options(
String modelNamePrefix,
String modelNameSuffix,

boolean implicitHeaders,
String implicitHeadersRegex,

boolean beanValidation,
boolean optional,
boolean reactive,
boolean useOneOfInterfaces,
boolean generateHttpResponseAlways,
boolean generateHttpResponseWhereRequired,
boolean generateSwaggerAnnotations,
TestFramework testFramework,
SerializationLibraryKind serializationLibraryKind,
MicronautCodeGeneratorOptionsBuilder.DateTimeFormat dateTimeFormat
Expand Down
Loading

0 comments on commit 25bb2a6

Please sign in to comment.