From dabf01c3fa457928858dd4074bfb46eba71b409e Mon Sep 17 00:00:00 2001 From: Jim Schubert Date: Fri, 9 Jun 2017 03:40:18 -0400 Subject: [PATCH] [kotlin] support collection format multi (#5792) * [kotlin] support collectionFormat:multi Adds "multi" support to collections. Also changes generic lists (List) to arrays. Generic lists and nested lists can be problematic and require customized json factories. The previous implement appeared to work because the results in the test were LinkedHashMap with count greather than 0. The functional test has been updated to force serialization and verify the results. * [kotlin] Regenerate sample * [kotlin] Update model test for Array changes --- .../languages/KotlinClientCodegen.java | 9 +++-- .../main/resources/kotlin-client/api.mustache | 10 +---- .../ApiAbstractions.kt.mustache | 20 ++++++++++ .../infrastructure/ApiClient.kt.mustache | 7 +++- .../infrastructure/RequestConfig.kt.mustache | 4 +- .../kotlin/KotlinClientCodegenModelTest.java | 4 +- samples/client/petstore/kotlin/docs/Pet.md | 4 +- samples/client/petstore/kotlin/docs/PetApi.md | 20 +++++----- .../client/petstore/kotlin/docs/UserApi.md | 8 ++-- .../kotlin/io/swagger/client/apis/PetApi.kt | 40 ++++++++----------- .../kotlin/io/swagger/client/apis/StoreApi.kt | 16 ++------ .../kotlin/io/swagger/client/apis/UserApi.kt | 28 +++++-------- .../client/infrastructure/ApiAbstractions.kt | 20 ++++++++++ .../client/infrastructure/ApiClient.kt | 7 +++- .../client/infrastructure/RequestConfig.kt | 4 +- .../kotlin/io/swagger/client/models/Pet.kt | 4 +- .../swagger/client/functional/EvaluateTest.kt | 7 +++- 17 files changed, 120 insertions(+), 92 deletions(-) create mode 100644 modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiAbstractions.kt.mustache create mode 100644 samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiAbstractions.kt diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java index 7e52a1c65541..2df436d195a3 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java @@ -125,16 +125,16 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig typeMapping.put("date-time", "java.time.LocalDateTime"); typeMapping.put("date", "java.time.LocalDateTime"); typeMapping.put("file", "java.io.File"); - typeMapping.put("array", "kotlin.collections.List"); - typeMapping.put("list", "kotlin.collections.List"); + typeMapping.put("array", "kotlin.Array"); + typeMapping.put("list", "kotlin.Array"); typeMapping.put("map", "kotlin.collections.Map"); typeMapping.put("object", "kotlin.Any"); typeMapping.put("binary", "kotlin.Array"); typeMapping.put("Date", "java.time.LocalDateTime"); typeMapping.put("DateTime", "java.time.LocalDateTime"); - instantiationTypes.put("array", "listOf"); - instantiationTypes.put("list", "listOf"); + instantiationTypes.put("array", "arrayOf"); + instantiationTypes.put("list", "arrayOf"); instantiationTypes.put("map", "mapOf"); importMapping = new HashMap(); @@ -241,6 +241,7 @@ public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", "/"); supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApiAbstractions.kt.mustache", infrastructureFolder, "ApiAbstractions.kt")); supportingFiles.add(new SupportingFile("infrastructure/ApiInfrastructureResponse.kt.mustache", infrastructureFolder, "ApiInfrastructureResponse.kt")); supportingFiles.add(new SupportingFile("infrastructure/ApplicationDelegates.kt.mustache", infrastructureFolder, "ApplicationDelegates.kt")); supportingFiles.add(new SupportingFile("infrastructure/RequestConfig.kt.mustache", infrastructureFolder, "RequestConfig.kt")); diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache index 545cd29e6916..cae4e8cb62b6 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache @@ -19,7 +19,7 @@ class {{classname}}(basePath: kotlin.String = "{{{basePath}}}") : ApiClient(base @Suppress("UNCHECKED_CAST"){{/returnType}} fun {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) : {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}} { val localVariableBody: kotlin.Any? = {{#hasBodyParam}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}null{{/hasFormParams}}{{#hasFormParams}}mapOf({{#formParams}}"{{{baseName}}}" to "${{paramName}}"{{#hasMore}}, {{/hasMore}}{{/formParams}}){{/hasFormParams}}{{/hasBodyParam}} - val localVariableQuery: kotlin.collections.Map = {{^hasQueryParams}}mapOf(){{/hasQueryParams}}{{#hasQueryParams}}mapOf({{#queryParams}}"{{paramName}}" to {{#isContainer}}{{paramName}}.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}{{#hasMore}}, {{/hasMore}}{{/queryParams}}){{/hasQueryParams}} + val localVariableQuery: MultiValueMap = {{^hasQueryParams}}mapOf(){{/hasQueryParams}}{{#hasQueryParams}}mapOf({{#queryParams}}"{{paramName}}" to {{#isContainer}}toMultiValue({{paramName}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf("${{paramName}}"){{/isContainer}}{{#hasMore}}, {{/hasMore}}{{/queryParams}}){{/hasQueryParams}} val localVariableHeaders: kotlin.collections.Map = {{^headerParams}}mapOf({{#hasFormParams}}"Content-Type" to "multipart/form-data"{{/hasFormParams}}){{/headerParams}}{{#headerParams}}mapOf("{{paramName}}" to {{#isContainer}}{{paramName}}.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}){{/headerParams}} val localVariableConfig = RequestConfig( RequestMethod.{{httpMethod}}, @@ -43,13 +43,5 @@ class {{classname}}(basePath: kotlin.String = "{{{basePath}}}") : ApiClient(base } {{/operation}} - - private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { - "csv" -> "," - "tsv" -> "\t" - "pipes" -> "|" - "ssv" -> " " - else -> "" - } } {{/operations}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiAbstractions.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiAbstractions.kt.mustache new file mode 100644 index 000000000000..0a42ce534d3d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiAbstractions.kt.mustache @@ -0,0 +1,20 @@ +package {{packageName}}.infrastructure + +typealias MultiValueMap = Map> + +fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipes" -> "|" + "ssv" -> " " + else -> "" +} + +val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } + +fun toMultiValue(items: List, collectionFormat: String, map: (item: Any?) -> String = defaultMultiValueConverter): List { + return when(collectionFormat) { + "multi" -> items.map(map) + else -> listOf(items.map(map).joinToString(separator = collectionDelimiter(collectionFormat))) + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache index 7480a80fce9a..49517bd15927 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache @@ -60,7 +60,11 @@ open class ApiClient(val baseUrl: String) { var urlBuilder = httpUrl.newBuilder() .addPathSegments(requestConfig.path.trimStart('/')) - requestConfig.query.forEach { k, v -> urlBuilder = urlBuilder.addQueryParameter(k,v) } + requestConfig.query.forEach { k, v -> + v.forEach { queryValue -> + urlBuilder = urlBuilder.addQueryParameter(k,queryValue) + } + } val url = urlBuilder.build() val headers = requestConfig.headers + defaultHeaders @@ -73,6 +77,7 @@ open class ApiClient(val baseUrl: String) { throw kotlin.IllegalStateException("Missing Accept header. This is required.") } + // TODO: support multiple contentType,accept options here. val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase() val accept = (headers[Accept] as String).substringBefore(";").toLowerCase() diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache index 913b42b648ed..58a3d605aa11 100644 --- a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache @@ -5,9 +5,11 @@ package {{packageName}}.infrastructure * NOTE: This object doesn't include 'body' because it * allows for caching of the constructed object * for many request definitions. + * NOTE: Headers is a Map because rfc2616 defines + * multi-valued headers as csv-only. */ data class RequestConfig( val method: RequestMethod, val path: String, val headers: Map = mapOf(), - val query: Map = mapOf()) \ No newline at end of file + val query: Map> = mapOf()) \ No newline at end of file diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java index 474806638d67..23bf434bf3fb 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java @@ -104,10 +104,10 @@ public class KotlinClientCodegenModelTest { Assert.assertEquals(property.baseName, "examples"); Assert.assertEquals(property.getter, "getExamples"); Assert.assertEquals(property.setter, "setExamples"); - Assert.assertEquals(property.datatype, "kotlin.collections.List"); + Assert.assertEquals(property.datatype, "kotlin.Array"); Assert.assertEquals(property.name, "examples"); Assert.assertEquals(property.defaultValue, "null"); - Assert.assertEquals(property.baseType, "kotlin.collections.List"); + Assert.assertEquals(property.baseType, "kotlin.Array"); Assert.assertEquals(property.containerType, "array"); Assert.assertFalse(property.required); Assert.assertTrue(property.isContainer); diff --git a/samples/client/petstore/kotlin/docs/Pet.md b/samples/client/petstore/kotlin/docs/Pet.md index 73963abeb042..ec7756007379 100644 --- a/samples/client/petstore/kotlin/docs/Pet.md +++ b/samples/client/petstore/kotlin/docs/Pet.md @@ -7,8 +7,8 @@ Name | Type | Description | Notes **id** | **kotlin.Long** | | [optional] **category** | [**Category**](Category.md) | | [optional] **name** | **kotlin.String** | | -**photoUrls** | **kotlin.collections.List<kotlin.String>** | | -**tags** | [**kotlin.collections.List<Tag>**](Tag.md) | | [optional] +**photoUrls** | **kotlin.Array<kotlin.String>** | | +**tags** | [**kotlin.Array<Tag>**](Tag.md) | | [optional] **status** | [**inline**](#StatusEnum) | pet status in the store | [optional] diff --git a/samples/client/petstore/kotlin/docs/PetApi.md b/samples/client/petstore/kotlin/docs/PetApi.md index 39803d4af412..29fb5bd0eeb8 100644 --- a/samples/client/petstore/kotlin/docs/PetApi.md +++ b/samples/client/petstore/kotlin/docs/PetApi.md @@ -110,7 +110,7 @@ null (empty response body) # **findPetsByStatus** -> kotlin.collections.List<Pet> findPetsByStatus(status) +> kotlin.Array<Pet> findPetsByStatus(status) Finds Pets by status @@ -123,9 +123,9 @@ Multiple status values can be provided with comma separated strings //import io.swagger.client.models.* val apiInstance = PetApi() -val status : kotlin.collections.List = // kotlin.collections.List | Status values that need to be considered for filter +val status : kotlin.Array = // kotlin.Array | Status values that need to be considered for filter try { - val result : kotlin.collections.List = apiInstance.findPetsByStatus(status) + val result : kotlin.Array = apiInstance.findPetsByStatus(status) println(result) } catch (e: ClientException) { println("4xx response calling PetApi#findPetsByStatus") @@ -140,11 +140,11 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **status** | [**kotlin.collections.List<kotlin.String>**](kotlin.String.md)| Status values that need to be considered for filter | [enum: available, pending, sold] + **status** | [**kotlin.Array<kotlin.String>**](kotlin.String.md)| Status values that need to be considered for filter | [enum: available, pending, sold] ### Return type -[**kotlin.collections.List<Pet>**](Pet.md) +[**kotlin.Array<Pet>**](Pet.md) ### Authorization @@ -157,7 +157,7 @@ Name | Type | Description | Notes # **findPetsByTags** -> kotlin.collections.List<Pet> findPetsByTags(tags) +> kotlin.Array<Pet> findPetsByTags(tags) Finds Pets by tags @@ -170,9 +170,9 @@ Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 //import io.swagger.client.models.* val apiInstance = PetApi() -val tags : kotlin.collections.List = // kotlin.collections.List | Tags to filter by +val tags : kotlin.Array = // kotlin.Array | Tags to filter by try { - val result : kotlin.collections.List = apiInstance.findPetsByTags(tags) + val result : kotlin.Array = apiInstance.findPetsByTags(tags) println(result) } catch (e: ClientException) { println("4xx response calling PetApi#findPetsByTags") @@ -187,11 +187,11 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **tags** | [**kotlin.collections.List<kotlin.String>**](kotlin.String.md)| Tags to filter by | + **tags** | [**kotlin.Array<kotlin.String>**](kotlin.String.md)| Tags to filter by | ### Return type -[**kotlin.collections.List<Pet>**](Pet.md) +[**kotlin.Array<Pet>**](Pet.md) ### Authorization diff --git a/samples/client/petstore/kotlin/docs/UserApi.md b/samples/client/petstore/kotlin/docs/UserApi.md index 0974c7f83cb4..d15aae23da39 100644 --- a/samples/client/petstore/kotlin/docs/UserApi.md +++ b/samples/client/petstore/kotlin/docs/UserApi.md @@ -75,7 +75,7 @@ Creates list of users with given input array //import io.swagger.client.models.* val apiInstance = UserApi() -val body : kotlin.collections.List = // kotlin.collections.List | List of user object +val body : kotlin.Array = // kotlin.Array | List of user object try { apiInstance.createUsersWithArrayInput(body) } catch (e: ClientException) { @@ -91,7 +91,7 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **body** | [**kotlin.collections.List<User>**](User.md)| List of user object | + **body** | [**kotlin.Array<User>**](User.md)| List of user object | ### Return type @@ -121,7 +121,7 @@ Creates list of users with given input array //import io.swagger.client.models.* val apiInstance = UserApi() -val body : kotlin.collections.List = // kotlin.collections.List | List of user object +val body : kotlin.Array = // kotlin.Array | List of user object try { apiInstance.createUsersWithListInput(body) } catch (e: ClientException) { @@ -137,7 +137,7 @@ try { Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - **body** | [**kotlin.collections.List<User>**](User.md)| List of user object | + **body** | [**kotlin.Array<User>**](User.md)| List of user object | ### Return type diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt index a01400aa9d97..c716883b3407 100644 --- a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt @@ -26,7 +26,7 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli */ fun addPet(body: Pet) : Unit { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -58,7 +58,7 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli */ fun deletePet(petId: kotlin.Long, apiKey: kotlin.String) : Unit { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf("apiKey" to apiKey) val localVariableConfig = RequestConfig( RequestMethod.DELETE, @@ -85,12 +85,12 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli * Finds Pets by status * Multiple status values can be provided with comma separated strings * @param status Status values that need to be considered for filter - * @return kotlin.collections.List + * @return kotlin.Array */ @Suppress("UNCHECKED_CAST") - fun findPetsByStatus(status: kotlin.collections.List) : kotlin.collections.List { + fun findPetsByStatus(status: kotlin.Array) : kotlin.Array { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf("status" to status.joinToString(separator = collectionDelimiter("csv"))) + val localVariableQuery: MultiValueMap = mapOf("status" to toMultiValue(status.toList(), "csv")) val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -98,13 +98,13 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli query = localVariableQuery, headers = localVariableHeaders ) - val response = request>( + val response = request>( localVariableConfig, localVariableBody ) return when (response.responseType) { - ResponseType.Success -> (response as Success<*>).data as kotlin.collections.List + ResponseType.Success -> (response as Success<*>).data as kotlin.Array ResponseType.Informational -> TODO() ResponseType.Redirection -> TODO() ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") @@ -117,12 +117,12 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli * Finds Pets by tags * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. * @param tags Tags to filter by - * @return kotlin.collections.List + * @return kotlin.Array */ @Suppress("UNCHECKED_CAST") - fun findPetsByTags(tags: kotlin.collections.List) : kotlin.collections.List { + fun findPetsByTags(tags: kotlin.Array) : kotlin.Array { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf("tags" to tags.joinToString(separator = collectionDelimiter("csv"))) + val localVariableQuery: MultiValueMap = mapOf("tags" to toMultiValue(tags.toList(), "csv")) val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -130,13 +130,13 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli query = localVariableQuery, headers = localVariableHeaders ) - val response = request>( + val response = request>( localVariableConfig, localVariableBody ) return when (response.responseType) { - ResponseType.Success -> (response as Success<*>).data as kotlin.collections.List + ResponseType.Success -> (response as Success<*>).data as kotlin.Array ResponseType.Informational -> TODO() ResponseType.Redirection -> TODO() ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") @@ -154,7 +154,7 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli @Suppress("UNCHECKED_CAST") fun getPetById(petId: kotlin.Long) : Pet { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -185,7 +185,7 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli */ fun updatePet(body: Pet) : Unit { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.PUT, @@ -218,7 +218,7 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli */ fun updatePetWithForm(petId: kotlin.Long, name: kotlin.String, status: kotlin.String) : Unit { val localVariableBody: kotlin.Any? = mapOf("name" to "$name", "status" to "$status") - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf("Content-Type" to "multipart/form-data") val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -252,7 +252,7 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli @Suppress("UNCHECKED_CAST") fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String, file: java.io.File) : ApiResponse { val localVariableBody: kotlin.Any? = mapOf("additionalMetadata" to "$additionalMetadata", "file" to "$file") - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf("Content-Type" to "multipart/form-data") val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -275,12 +275,4 @@ class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCli } } - - private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { - "csv" -> "," - "tsv" -> "\t" - "pipes" -> "|" - "ssv" -> " " - else -> "" - } } diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt index 9dabfdb3b66f..53e3813f4dc2 100644 --- a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt @@ -25,7 +25,7 @@ class StoreApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiC */ fun deleteOrder(orderId: kotlin.String) : Unit { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.DELETE, @@ -56,7 +56,7 @@ class StoreApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiC @Suppress("UNCHECKED_CAST") fun getInventory() : kotlin.collections.Map { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -88,7 +88,7 @@ class StoreApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiC @Suppress("UNCHECKED_CAST") fun getOrderById(orderId: kotlin.Long) : Order { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -120,7 +120,7 @@ class StoreApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiC @Suppress("UNCHECKED_CAST") fun placeOrder(body: Order) : Order { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -143,12 +143,4 @@ class StoreApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiC } } - - private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { - "csv" -> "," - "tsv" -> "\t" - "pipes" -> "|" - "ssv" -> " " - else -> "" - } } diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt index e703c71bcddc..d3d80fe76ab6 100644 --- a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt @@ -25,7 +25,7 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl */ fun createUser(body: User) : Unit { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -54,9 +54,9 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl * @param body List of user object * @return void */ - fun createUsersWithArrayInput(body: kotlin.collections.List) : Unit { + fun createUsersWithArrayInput(body: kotlin.Array) : Unit { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -85,9 +85,9 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl * @param body List of user object * @return void */ - fun createUsersWithListInput(body: kotlin.collections.List) : Unit { + fun createUsersWithListInput(body: kotlin.Array) : Unit { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.POST, @@ -118,7 +118,7 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl */ fun deleteUser(username: kotlin.String) : Unit { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.DELETE, @@ -150,7 +150,7 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl @Suppress("UNCHECKED_CAST") fun getUserByName(username: kotlin.String) : User { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -183,7 +183,7 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl @Suppress("UNCHECKED_CAST") fun loginUser(username: kotlin.String, password: kotlin.String) : kotlin.String { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf("username" to username, "password" to password) + val localVariableQuery: MultiValueMap = mapOf("username" to listOf("$username"), "password" to listOf("$password")) val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -213,7 +213,7 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl */ fun logoutUser() : Unit { val localVariableBody: kotlin.Any? = null - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.GET, @@ -245,7 +245,7 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl */ fun updateUser(username: kotlin.String, body: User) : Unit { val localVariableBody: kotlin.Any? = body - val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableQuery: MultiValueMap = mapOf() val localVariableHeaders: kotlin.collections.Map = mapOf() val localVariableConfig = RequestConfig( RequestMethod.PUT, @@ -268,12 +268,4 @@ class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiCl } } - - private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { - "csv" -> "," - "tsv" -> "\t" - "pipes" -> "|" - "ssv" -> " " - else -> "" - } } diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiAbstractions.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiAbstractions.kt new file mode 100644 index 000000000000..87cc13d045b2 --- /dev/null +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiAbstractions.kt @@ -0,0 +1,20 @@ +package io.swagger.client.infrastructure + +typealias MultiValueMap = Map> + +fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipes" -> "|" + "ssv" -> " " + else -> "" +} + +val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } + +fun toMultiValue(items: List, collectionFormat: String, map: (item: Any?) -> String = defaultMultiValueConverter): List { + return when(collectionFormat) { + "multi" -> items.map(map) + else -> listOf(items.map(map).joinToString(separator = collectionDelimiter(collectionFormat))) + } +} \ No newline at end of file diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt index 05ff12c0f94e..40d771e475d7 100644 --- a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt @@ -60,7 +60,11 @@ open class ApiClient(val baseUrl: String) { var urlBuilder = httpUrl.newBuilder() .addPathSegments(requestConfig.path.trimStart('/')) - requestConfig.query.forEach { k, v -> urlBuilder = urlBuilder.addQueryParameter(k,v) } + requestConfig.query.forEach { k, v -> + v.forEach { queryValue -> + urlBuilder = urlBuilder.addQueryParameter(k,queryValue) + } + } val url = urlBuilder.build() val headers = requestConfig.headers + defaultHeaders @@ -73,6 +77,7 @@ open class ApiClient(val baseUrl: String) { throw kotlin.IllegalStateException("Missing Accept header. This is required.") } + // TODO: support multiple contentType,accept options here. val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase() val accept = (headers[Accept] as String).substringBefore(";").toLowerCase() diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt index 1df8b769db56..3825588ce4af 100644 --- a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt @@ -5,9 +5,11 @@ package io.swagger.client.infrastructure * NOTE: This object doesn't include 'body' because it * allows for caching of the constructed object * for many request definitions. + * NOTE: Headers is a Map because rfc2616 defines + * multi-valued headers as csv-only. */ data class RequestConfig( val method: RequestMethod, val path: String, val headers: Map = mapOf(), - val query: Map = mapOf()) \ No newline at end of file + val query: Map> = mapOf()) \ No newline at end of file diff --git a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt index 5fe800afe777..68ee9be1b4bd 100644 --- a/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt +++ b/samples/client/petstore/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt @@ -25,10 +25,10 @@ import io.swagger.client.models.Tag */ data class Pet ( val name: kotlin.String, - val photoUrls: kotlin.collections.List, + val photoUrls: kotlin.Array, val id: kotlin.Long? = null, val category: Category? = null, - val tags: kotlin.collections.List? = null, + val tags: kotlin.Array? = null, /* pet status in the store */ val status: Pet.Status? = null ) { diff --git a/samples/client/petstore/kotlin/src/test/kotlin/io/swagger/client/functional/EvaluateTest.kt b/samples/client/petstore/kotlin/src/test/kotlin/io/swagger/client/functional/EvaluateTest.kt index 955560fb798d..db40b04f2ef7 100644 --- a/samples/client/petstore/kotlin/src/test/kotlin/io/swagger/client/functional/EvaluateTest.kt +++ b/samples/client/petstore/kotlin/src/test/kotlin/io/swagger/client/functional/EvaluateTest.kt @@ -2,16 +2,21 @@ package io.swagger.client.functional import io.kotlintest.matchers.should import io.kotlintest.matchers.beGreaterThan +import io.kotlintest.matchers.shouldEqual import io.kotlintest.specs.ShouldSpec import io.swagger.client.apis.PetApi +import io.swagger.client.models.Pet class EvaluateTest : ShouldSpec() { init { should("query against pet statuses") { val api = PetApi() - val results = api.findPetsByStatus(listOf("sold")) + val results = api.findPetsByStatus(arrayOf("sold")) results.size should beGreaterThan(1) + + // Pet is lazily deserialized here. Need to iterate to verify all "sold" statuses. + results.all { it.status == Pet.Status.sold } shouldEqual true } // TODO: Handle default (200) response