-
-
Notifications
You must be signed in to change notification settings - Fork 6.6k
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
[Kotlin] Use mutable container types when 'modelMutable' is enabled #11154
[Kotlin] Use mutable container types when 'modelMutable' is enabled #11154
Conversation
3f56d5f
to
d569dfb
Compare
modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache
Show resolved
Hide resolved
2570e60
to
1e3423f
Compare
@@ -566,6 +573,8 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert | |||
resp.code = "200"; | |||
} | |||
|
|||
resp.baseType = getNonMutableContainerTypeIfNeeded(resp.baseType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rm3l could you please explain what this does and why it's needed? Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar reasons as in my other comment below.
For example, given the following input operation:
/store/inventory:
get:
tags:
- store
summary: Returns pet inventories by status
description: Returns a map of status codes to quantities
operationId: getInventory
produces:
- application/json
parameters: []
responses:
'200':
description: successful operation
schema:
type: object
additionalProperties:
type: integer
format: int32
Without the changes here, this resulted in the following code generated by the kotlin-spring
generator:
StoreApiController.kt
: the Response field in theApiResponse
annotation has changed toMutableMap
, which, I guess, is not valid.
diff --git a/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/StoreApiController.kt b/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/StoreApiController.kt
index e2be73b4ec3..ad63efeae2d 100644
--- a/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/StoreApiController.kt
+++ b/samples/server/petstore/kotlin-springboot-modelMutable/src/main/kotlin/org/openapitools/api/StoreApiController.kt
@@ -58,7 +58,7 @@ class StoreApiController(@Autowired(required = true) val service: StoreApiServic
responseContainer = "Map",
authorizations = [Authorization(value = "api_key")])
@ApiResponses(
value = [ApiResponse(code = 200, message = "successful operation",
- response = kotlin.collections.Map::class,
responseContainer = "Map")])
value = [ApiResponse(code = 200, message = "successful operation",
+ response = kotlin.collections.MutableMap::class,
responseContainer = "Map")])
@RequestMapping(
method = [RequestMethod.GET],
value = ["/store/inventory"],
Again, maybe this is not the right approach here or maybe I missed something, but I found no clear way to distinguish between the type mappings used for generating models (Kotlin data classes) and response types in the kotlin-spring
generator. Also, since the original issue I reported was more related to model mutability, I thought it would be ok not to change the code generated for the Spring Controllers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining, this way we have an explanation in case we need it 👍
final List<CodegenParameter> allParams = operation.allParams; | ||
if (allParams != null) { | ||
allParams.forEach(param -> | ||
param.dataType = getNonMutableContainerTypeIfNeeded(param.dataType)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rm3l could you please explain what this does and why it's needed? Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. While testing, I noticed that changing the type mappings to their Mutable types resulted in all types being changed as well in the generated Spring Request handlers methods.
This included the types for all query parameters. On the other side, the generated tests would still use the non-mutable types, resulting in the generated test code not compiling at all.
For example, given the following operation in an OpenAPI definition:
/pet/findByStatus:
get:
operationId: findPetsByStatus
parameters:
- name: status
in: query
required: true
type: array
items:
type: string
enum:
- available
- pending
- sold
Without the changes here, this resulted in the following code generated by the kotlin-spring
generator:
PetApiController.kt
(notice thatstatus
is generated as aMutableList
):
@RequestMapping(
method = [RequestMethod.GET],
value = ["/pet/findByStatus"]
)
fun findPetsByStatus(...
@RequestParam(value = "status", required = true)
status: kotlin.collections.MutableList<kotlin.String>
): ResponseEntity<List<Pet>> {
return ResponseEntity(service.findPetsByStatus(status), HttpStatus.valueOf(200))
}
PetApiTest.kt
(which would not compile becauseapi.findPetsByStatus
expects aMutableList
, butstatus
is suprisingly generated as aList
)
@Test
fun findPetsByStatusTest() {
val status:kotlin.collections.List<kotlin.String> = TODO()
val response: ResponseEntity<List<Pet>> = api.findPetsByStatus(status)
// TODO: test validations
}
Maybe this is not the right approach here or maybe I missed something, but I found no clear way to distinguish between the type mappings used for generating models (Kotlin data classes) and request parameters in the kotlin-spring
generator. Also, since the original issue I reported was more related to model mutability, I thought it would be ok not to change the code generated for the Spring Controllers.
Another possible approach I thought of that would fix the compilation issue was to make sure the test code generates request params as mutable containers instead.
Let me know your thoughts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining, this way we have an explanation in case we need it 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, I added few comments in the code for reference.
@field:JsonProperty("photoUrls", required = true) var photoUrls: kotlin.collections.List<kotlin.String>, | ||
@field:JsonProperty("photoUrls", required = true) var photoUrls: kotlin.collections.MutableList<kotlin.String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rm3l As far as I can tell the mutable models are disabled by default, and since there is not sample project with it active, this change shouldn't happen, or am I wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file got changed right after I launched the bin/generate-samples.sh
script, as recommended in the PR checklist.
From what I understood after reading the script, it generates samples for all configs under the bin/configs
folder. It therefore picks the kotlin-spring-boot-modelMutable.yaml
file, which generates a sample project with mutable models.
So I guess mutable models are indeed disabled by default, except in this sample project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining 👍
@rm3l Could you please enable the mutable models in a sample project for this feature to be testes in every commit please? |
Sure. Indeed, since the existing |
1e3423f
to
de65993
Compare
@4brunu I added additional configs to generate samples with model mutability enabled for all the other Kotlin generators impacted by this PR:
Could you please take a look when you get a chance? Thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the changes.
Looks good to me 👍
…del_mutable_is_true
Great - thanks for your time and support, @4brunu! |
Hi, it works great for MutableLists with default values, but unfortunately not for MutableSets with default values. As example, this is what currently will be generated when modelMutable is set to true:
My OAS:
|
if (end > 0) { | ||
dataTypeAssigner.setReturnType(returnType.substring("kotlin.collections.MutableMap<".length(), end).split(",")[1].trim()); | ||
dataTypeAssigner.setReturnContainer("Map"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is a case missing for "MutableSet"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @martin-regis could you please open a PR with a fix please?
This should close #11088.
Basically, when the Kotlin generators are run with
modelMutable
set totrue
, the changes here allow to generate read-writeMutable
Kotlin container types (likeMutableList
orMutableMap
) for OpenAPI arrays and dictionaries, instead of read-only types (likeList
orMap
).This way, it makes it possible to mutate such containers right away, without performing potentially expensive copies.
See #11088 for more context and details.
Maybe I missed something, but I noticed that unlike the other Kotlin generators, the
kotlin-client
one uses the raw templates rather than the Java source files to determine the different data types.Please do let me know if things should be done differently.
PR checklist
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*
.For Windows users, please run the script in Git BASH.
master
(5.3.0),6.0.x
@jimschubert @dr4ke616 @karismann @Zomzog @andrewemery @4brunu @yutaka0m Could anyone of you please take a look at this ? Thanks.