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 @@ -18,4 +18,5 @@ additionalProperties:
# performBeanValidation should default to "false" when not specified
useResponseEntity: "false"
useSpringBoot3: "true"
useTags: "true"

2 changes: 1 addition & 1 deletion bin/configs/spring-http-interface-reactive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ additionalProperties:
# useBeanValidation should default to "false" when not specified
# performBeanValidation should default to "false" when not specified
useSpringBoot3: "true"

useTags: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ additionalProperties:
performBeanValidation: "true"
useHttpServiceProxyFactoryInterfacesConfigurator: "true"
useSpringBoot3: "true"
useTags: "true"
1 change: 1 addition & 0 deletions bin/configs/spring-http-interface.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ additionalProperties:
# useBeanValidation should default to "false" when not specified
# performBeanValidation should default to "false" when not specified
useSpringBoot3: "true"
useTags: "true"
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@ protected ImmutableMap.Builder<String, Lambda> addMustacheLambdas() {

@Override
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map<String, List<CodegenOperation>> operations) {
if (library.equals(SPRING_BOOT) && !useTags) {
if ((library.equals(SPRING_BOOT) || library.equals(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY)) && !useTags) {
String basePath = resourcePath;
if (basePath.startsWith("/")) {
basePath = basePath.substring(1);
Expand All @@ -1080,6 +1080,9 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera
basePath = "default";
} else {
co.subresourceOperation = !co.path.isEmpty();
// sanitize the raw path segment so it can be safely used as a Java identifier
// (e.g. "another-fake" -> "anotherFake") when deriving classVarName etc.
basePath = camelize(sanitizeName(basePath), LOWERCASE_FIRST_LETTER);
}
List<CodegenOperation> opList = operations.computeIfAbsent(basePath, k -> new ArrayList<>());
opList.add(co);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,9 @@ protected void applyJackson3Package() {
}

private boolean supportLibraryUseTags() {
return SPRING_BOOT.equals(library) || SPRING_CLOUD_LIBRARY.equals(library);
return SPRING_BOOT.equals(library)
|| SPRING_CLOUD_LIBRARY.equals(library)
|| SPRING_HTTP_INTERFACE.equals(library);
}

/**
Expand Down Expand Up @@ -927,6 +929,9 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera
basePath = "default";
} else {
co.subresourceOperation = !co.path.isEmpty();
// sanitize the raw path segment so it can be safely used as a Java identifier
// (e.g. "another-fake" -> "anotherFake") when deriving classVarName etc.
basePath = camelize(sanitizeName(basePath), LOWERCASE_FIRST_LETTER);
}
final List<CodegenOperation> opList = operations.computeIfAbsent(basePath, k -> new ArrayList<>());
opList.add(co);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,61 @@ public void shouldNotUseTagsForClassname() throws IOException {
assertThat(notExisting).isNull();
}

@Test
public void useTags_false_groupsByFirstPathSegment_springHttpInterface() {
SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.additionalProperties().put(USE_TAGS, "false");
codegen.processOpts();

CodegenOperation co = new CodegenOperation();
co.operationId = "findByStatus";
co.path = "/pet/findByStatus";
Map<String, List<CodegenOperation>> groups = new HashMap<>();

codegen.addOperationToGroup("Pet", "/pet/findByStatus", new Operation(), co, groups);

assertTrue(groups.containsKey("pet"));
assertEquals(co.baseName, "pet");
}

@Test
public void useTags_true_groupsByTag_springHttpInterface() {
SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.additionalProperties().put(USE_TAGS, "true");
codegen.processOpts();

CodegenOperation co = new CodegenOperation();
co.operationId = "findByStatus";
Map<String, List<CodegenOperation>> groups = new HashMap<>();

codegen.addOperationToGroup("Pet", "/pet/findByStatus", new Operation(), co, groups);

assertTrue(groups.containsKey("Pet"));
}

@Test
public void useTags_false_groupsByFirstPathSegment_sanitizesInvalidIdentifierChars_springHttpInterface() {
SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary(SPRING_HTTP_INTERFACE);
codegen.additionalProperties().put(USE_TAGS, "false");
codegen.processOpts();

CodegenOperation co = new CodegenOperation();
co.operationId = "dummy";
co.path = "/another-fake/dummy";
Map<String, List<CodegenOperation>> groups = new HashMap<>();

codegen.addOperationToGroup("$another-fake?", "/another-fake/dummy", new Operation(), co, groups);

// the first path segment "another-fake" must be sanitized into a valid Java identifier
// (no hyphen) instead of being used as-is, which previously produced e.g.
// "AnotherFakeApi another-fakeHttpProxy()" - invalid Java syntax.
assertTrue(groups.containsKey("anotherFake"));
assertEquals(co.baseName, "anotherFake");
}

@Test
public void shouldAddValidAnnotationIntoCollectionWhenBeanValidationIsEnabled_issue14723() throws IOException {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package org.openapitools.codegen.kotlin;

import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.languages.KotlinSpringServerCodegen;
import org.testng.annotations.Test;

import io.swagger.v3.oas.models.Operation;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.openapitools.codegen.CodegenConstants.INTERFACE_ONLY;
import static org.openapitools.codegen.languages.KotlinSpringServerCodegen.SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY;
import static org.openapitools.codegen.languages.KotlinSpringServerCodegen.USE_TAGS;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

public class KotlinSpringServerCodegenTest {
Expand Down Expand Up @@ -203,4 +212,58 @@ public void shouldDisableBuiltInValidationOptionByDefault() throws IOException {
TestUtils.assertFileContains(userApiKt, "@Validated");
}

@Test(description = "useTags=false should group operations by first path segment for spring-declarative-http-interface")
public void useTags_false_groupsByFirstPathSegment_springDeclarativeHttpInterface() {
KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setLibrary(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY);
codegen.additionalProperties().put(USE_TAGS, false);
codegen.processOpts();

CodegenOperation co = new CodegenOperation();
co.operationId = "findByStatus";
co.path = "/pet/findByStatus";
Map<String, List<CodegenOperation>> groups = new HashMap<>();

codegen.addOperationToGroup("Pet", "/pet/findByStatus", new Operation(), co, groups);

assertTrue(groups.containsKey("pet"));
assertEquals(co.baseName, "pet");
}

@Test(description = "useTags=true should group operations by tag for spring-declarative-http-interface")
public void useTags_true_groupsByTag_springDeclarativeHttpInterface() {
KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setLibrary(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY);
codegen.additionalProperties().put(USE_TAGS, true);
codegen.processOpts();

CodegenOperation co = new CodegenOperation();
co.operationId = "findByStatus";
Map<String, List<CodegenOperation>> groups = new HashMap<>();

codegen.addOperationToGroup("Pet", "/pet/findByStatus", new Operation(), co, groups);

assertTrue(groups.containsKey("Pet"));
}

@Test(description = "useTags=false should sanitize invalid identifier chars from the first path segment for spring-declarative-http-interface")
public void useTags_false_groupsByFirstPathSegment_sanitizesInvalidIdentifierChars_springDeclarativeHttpInterface() {
KotlinSpringServerCodegen codegen = new KotlinSpringServerCodegen();
codegen.setLibrary(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY);
codegen.additionalProperties().put(USE_TAGS, false);
codegen.processOpts();

CodegenOperation co = new CodegenOperation();
co.operationId = "dummy";
co.path = "/another-fake/dummy";
Map<String, List<CodegenOperation>> groups = new HashMap<>();

codegen.addOperationToGroup("$another-fake?", "/another-fake/dummy", new Operation(), co, groups);

// the first path segment "another-fake" must be sanitized into a valid Kotlin/Java
// identifier (no hyphen) instead of being used as-is.
assertTrue(groups.containsKey("anotherFake"));
assertEquals(co.baseName, "anotherFake");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1087,14 +1087,21 @@ public void givenMultipartForm_whenGenerateReactiveDeclarativeHttpInterface_then
new HashMap<>(),
configurator -> configurator.setLibrary(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY));

Path apiFile = files.get("MultipartApi.kt").toPath();
assertFileContains(apiFile,
"files: Array<org.springframework.web.multipart.MultipartFile>",
// With useTags=false (the library default), operations are grouped by first path
// segment rather than by tag, so the multipart operations are split across
// MultipartArrayApi.kt/MultipartSingleApi.kt/MultipartMixedApi.kt instead of a single
// tag-derived MultipartApi.kt.
Path arrayFile = files.get("MultipartArrayApi.kt").toPath();
Path mixedFile = files.get("MultipartMixedApi.kt").toPath();

assertFileContains(arrayFile, "files: Array<org.springframework.web.multipart.MultipartFile>");
assertFileContains(mixedFile,
"file: org.springframework.web.multipart.MultipartFile",
"status: MultipartMixedStatus",
"marker: MultipartMixedRequestMarker?",
"statusArray: kotlin.collections.List<MultipartMixedStatus>?");
assertFileNotContains(apiFile, "org.springframework.http.codec.multipart.Part");
assertFileNotContains(arrayFile, "org.springframework.http.codec.multipart.Part");
assertFileNotContains(mixedFile, "org.springframework.http.codec.multipart.Part");
}

private void assertReactiveMultipartParameters(Map<String, File> files, String fileSuffix) {
Expand Down Expand Up @@ -6640,7 +6647,7 @@ public void testSealedResponseInterfacesWithDeclarativeHttpInterface() throws IO
DefaultGenerator generator = new DefaultGenerator();
generator.opts(input).generate();

assertFileContains(Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/DefaultApi.kt"),
assertFileContains(Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/UsersApi.kt"),
"import org.openapitools.model.CreateUserResponse",
"import org.openapitools.model.GetUserResponse",
"fun createUser(",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ README.md
pom.xml
src/main/java/org/openapitools/api/AnotherFakeApi.java
src/main/java/org/openapitools/api/FakeApi.java
src/main/java/org/openapitools/api/FakeClassnameTags123Api.java
src/main/java/org/openapitools/api/FakeClassnameTestApi.java
src/main/java/org/openapitools/api/PetApi.java
src/main/java/org/openapitools/api/StoreApi.java
src/main/java/org/openapitools/api/UserApi.java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package org.openapitools.api;

import org.openapitools.model.ApiResponseDto;
import java.math.BigDecimal;
import org.openapitools.model.ChildWithNullableDto;
import org.openapitools.model.ClientDto;
Expand All @@ -15,6 +16,7 @@
import org.springframework.lang.Nullable;
import java.time.OffsetDateTime;
import org.openapitools.model.OuterCompositeDto;
import org.openapitools.model.ResponseObjectWithDifferentFieldNamesDto;
import org.openapitools.model.UserDto;
import org.openapitools.model.XmlItemDto;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -125,6 +127,22 @@ ResponseEntity<String> fakeOuterStringSerialize(
);


/**
* GET /fake/{petId}/response-object-different-names
*
* @param petId ID of pet to update (required)
* @return successful operation (status code 200)
*/
@HttpExchange(
method = "GET",
value = "/fake/{petId}/response-object-different-names",
accept = { "application/json" }
)
ResponseEntity<ResponseObjectWithDifferentFieldNamesDto> responseObjectDifferentNames(
@PathVariable("petId") Long petId
);


/**
* PUT /fake/body-with-file-schema
* For this test, the body for this request much reference a schema named &#x60;File&#x60;.
Expand Down Expand Up @@ -379,4 +397,26 @@ ResponseEntity<Integer> testWithResultExample(

);


/**
* POST /fake/{petId}/uploadImageWithRequiredFile : uploads an image (required)
*
*
* @param petId ID of pet to update (required)
* @param requiredFile file to upload (required)
* @param additionalMetadata Additional data to pass to server (optional)
* @return successful operation (status code 200)
*/
@HttpExchange(
method = "POST",
value = "/fake/{petId}/uploadImageWithRequiredFile",
accept = { "application/json" },
contentType = "multipart/form-data"
)
ResponseEntity<ApiResponseDto> uploadFileWithRequiredFile(
@PathVariable("petId") Long petId,
@RequestPart(value = "requiredFile", required = true) MultipartFile requiredFile,
@Valid @RequestParam(value = "additionalMetadata", required = false) String additionalMetadata
);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (7.26.0-SNAPSHOT).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.api;

import org.openapitools.model.ClientDto;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.service.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import jakarta.annotation.Generated;


@Validated
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.26.0-SNAPSHOT")
public interface FakeClassnameTestApi {

/**
* PATCH /fake_classname_test : To test class name in snake case
* To test class name in snake case
*
* @param clientDto client model (required)
* @return successful operation (status code 200)
*/
@HttpExchange(
method = "PATCH",
value = "/fake_classname_test",
accept = { "application/json" },
contentType = "application/json"
)
ResponseEntity<ClientDto> testClassname(
@Valid @RequestBody ClientDto clientDto
);

}
Loading
Loading