From fe10161277578589d5591446f4c67fe71019647e Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Wed, 18 Sep 2024 14:57:19 +0200 Subject: [PATCH 01/13] Create new generator --- bin/configs/tidal-kotlin-petstore-new.yaml | 6 + .../languages/TidalKotlinClientCodegen.java | 1037 +++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 3 + .../resources/tidal-kotlin/README.mustache | 113 ++ .../additionalModelTypeAnnotations.mustache | 2 + .../tidal-kotlin/anyof_class.mustache | 241 ++++ .../resources/tidal-kotlin/api_doc.mustache | 92 ++ .../resources/tidal-kotlin/api_test.mustache | 33 + .../tidal-kotlin/build.gradle.mustache | 216 ++++ .../resources/tidal-kotlin/class_doc.mustache | 15 + .../tidal-kotlin/data_class.mustache | 363 ++++++ .../tidal-kotlin/data_class_opt_var.mustache | 21 + .../tidal-kotlin/data_class_req_var.mustache | 21 + .../tidal-kotlin/enum_class.mustache | 113 ++ .../resources/tidal-kotlin/enum_doc.mustache | 7 + .../resources/tidal-kotlin/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle-wrapper.properties.mustache | 12 + .../tidal-kotlin/gradlew.bat.mustache | 89 ++ .../resources/tidal-kotlin/gradlew.mustache | 249 ++++ .../ApiAbstractions.kt.mustache | 23 + .../infrastructure/PartConfig.kt.mustache | 11 + .../infrastructure/RequestConfig.kt.mustache | 19 + .../infrastructure/RequestMethod.kt.mustache | 8 + .../tidal-kotlin/interface_opt_var.mustache | 18 + .../tidal-kotlin/interface_req_var.mustache | 18 + .../AtomicBooleanAdapter.kt.mustache | 21 + .../AtomicIntegerAdapter.kt.mustache | 21 + .../AtomicLongAdapter.kt.mustache | 21 + .../BigDecimalAdapter.kt.mustache | 38 + .../BigIntegerAdapter.kt.mustache | 43 + .../ByteArrayAdapter.kt.mustache | 50 + .../infrastructure/InstantAdapter.kt.mustache | 56 + .../LocalDateAdapter.kt.mustache | 103 ++ .../LocalDateTimeAdapter.kt.mustache | 103 ++ .../LocalTimeAdapter.kt.mustache | 56 + .../OffsetDateTimeAdapter.kt.mustache | 86 ++ .../infrastructure/Serializer.kt.mustache | 156 +++ .../SerializerHelper.kt.mustache | 50 + .../StringBuilderAdapter.kt.mustache | 20 + .../infrastructure/URIAdapter.kt.mustache | 38 + .../infrastructure/URLAdapter.kt.mustache | 21 + .../infrastructure/UUIDAdapter.kt.mustache | 40 + .../proguard-rules.pro.mustache | 11 + .../libraries/jvm-ktor/api.mustache | 152 +++ .../jvm-ktor/auth/ApiKeyAuth.kt.mustache | 16 + .../jvm-ktor/auth/Authentication.kt.mustache | 13 + .../jvm-ktor/auth/HttpBasicAuth.kt.mustache | 17 + .../jvm-ktor/auth/HttpBearerAuth.kt.mustache | 14 + .../libraries/jvm-ktor/auth/OAuth.kt.mustache | 10 + .../infrastructure/ApiClient.kt.mustache | 229 ++++ .../infrastructure/HttpResponse.kt.mustache | 51 + .../libraries/jvm-okhttp/api.mustache | 248 ++++ .../infrastructure/ApiClient.kt.mustache | 448 +++++++ .../infrastructure/ApiResponse.kt.mustache | 43 + .../infrastructure/Errors.kt.mustache | 18 + .../ResponseExtensions.kt.mustache | 24 + .../libraries/jvm-retrofit2/api.mustache | 161 +++ .../libraries/jvm-retrofit2/api_doc.mustache | 88 ++ .../jvm-retrofit2/auth/ApiKeyAuth.kt.mustache | 50 + .../auth/HttpBasicAuth.kt.mustache | 33 + .../auth/HttpBearerAuth.kt.mustache | 39 + .../jvm-retrofit2/auth/OAuth.kt.mustache | 151 +++ .../jvm-retrofit2/auth/OAuthFlow.kt.mustache | 5 + .../auth/OAuthOkHttpClient.kt.mustache | 61 + .../jvm-retrofit2/bodyParams.mustache | 1 + .../jvm-retrofit2/explodedQueryParam.mustache | 1 + .../jvm-retrofit2/formParams.mustache | 1 + .../jvm-retrofit2/headerParams.mustache | 1 + .../infrastructure/ApiClient.kt.mustache | 412 +++++++ .../CollectionFormats.kt.mustache | 56 + .../infrastructure/ResponseExt.kt.mustache | 35 + .../jvm-retrofit2/paramJavadoc.mustache | 5 + .../jvm-retrofit2/pathParams.mustache | 1 + .../jvm-retrofit2/queryParam.mustache | 1 + .../jvm-retrofit2/queryParams.mustache | 1 + .../jvm-spring-restclient/api.mustache | 147 +++ .../infrastructure/ApiClient.kt.mustache | 68 ++ .../jvm-spring-webclient/api.mustache | 149 +++ .../infrastructure/ApiClient.kt.mustache | 69 ++ .../libraries/jvm-vertx/api.mustache | 232 ++++ .../infrastructure/ApiClient.kt.mustache | 110 ++ .../infrastructure/ApiResponse.kt.mustache | 43 + .../infrastructure/Errors.kt.mustache | 18 + .../libraries/jvm-volley/README.mustache | 230 ++++ .../libraries/jvm-volley/api.mustache | 116 ++ .../libraries/jvm-volley/api_doc.mustache | 83 ++ .../jvm-volley/auth/apikeyauth.mustache | 16 + .../jvm-volley/auth/authentication.mustache | 15 + .../jvm-volley/auth/httpbasicauth.mustache | 3 + .../libraries/jvm-volley/auth/oauth.mustache | 5 + .../libraries/jvm-volley/bodyParams.mustache | 1 + .../libraries/jvm-volley/build.mustache | 111 ++ .../libraries/jvm-volley/formParams.mustache | 1 + .../jvm-volley/gradle.properties.mustache | 4 + .../jvm-volley/headerParams.mustache | 1 + .../CollectionFormats.kt.mustache | 56 + .../ITransformForStorage.mustache | 9 + .../libraries/jvm-volley/manifest.mustache | 5 + .../libraries/jvm-volley/pathParams.mustache | 1 + .../libraries/jvm-volley/queryParams.mustache | 1 + .../jvm-volley/request/GsonRequest.mustache | 119 ++ .../request/IRequestFactory.mustache | 64 + .../request/RequestFactory.mustache | 69 ++ .../libraries/multiplatform/api.mustache | 135 +++ .../multiplatform/auth/ApiKeyAuth.kt.mustache | 16 + .../auth/Authentication.kt.mustache | 13 + .../auth/HttpBasicAuth.kt.mustache | 17 + .../auth/HttpBearerAuth.kt.mustache | 14 + .../multiplatform/auth/OAuth.kt.mustache | 10 + .../multiplatform/build.gradle.kts.mustache | 103 ++ .../commonTest/Coroutine.kt.mustache | 13 + .../infrastructure/ApiClient.kt.mustache | 199 ++++ .../Base64ByteArray.kt.mustache | 29 + .../infrastructure/Bytes.kt.mustache | 101 ++ .../infrastructure/HttpResponse.kt.mustache | 51 + .../infrastructure/OctetByteArray.kt.mustache | 29 + .../iosTest/Coroutine.kt.mustache | 8 + .../jsTest/Coroutine.kt.mustache | 7 + .../jvmTest/Coroutine.kt.mustache | 8 + .../libraries/multiplatform/pom.mustache | 64 + .../serial_wrapper_request_list.mustache | 9 + .../serial_wrapper_request_map.mustache | 9 + .../serial_wrapper_response_list.mustache | 9 + .../serial_wrapper_response_map.mustache | 9 + .../settings.gradle.kts.mustache | 1 + .../tidal-kotlin/licenseInfo.mustache | 14 + .../resources/tidal-kotlin/model.mustache | 16 + .../tidal-kotlin/modelMutable.mustache | 1 + .../resources/tidal-kotlin/model_doc.mustache | 3 + .../tidal-kotlin/model_room.mustache | 38 + .../tidal-kotlin/model_room_init_var.mustache | 1 + .../tidal-kotlin/model_test.mustache | 29 + .../tidal-kotlin/oneof_class.mustache | 239 ++++ .../tidal-kotlin/param_default_value.mustache | 1 + .../tidal-kotlin/settings.gradle.mustache | 1 + .../tidal-kotlin/typeInfoAnnotation.mustache | 6 + 136 files changed, 8999 insertions(+) create mode 100644 bin/configs/tidal-kotlin-petstore-new.yaml create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.jar create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache create mode 100755 modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache diff --git a/bin/configs/tidal-kotlin-petstore-new.yaml b/bin/configs/tidal-kotlin-petstore-new.yaml new file mode 100644 index 000000000000..32a6f6212ccd --- /dev/null +++ b/bin/configs/tidal-kotlin-petstore-new.yaml @@ -0,0 +1,6 @@ +generatorName: tidal-kotlin +outputDir: samples/client/petstore/tidal/kotlin +inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/tidal-kotlin +additionalProperties: + hideGenerationTimestamp: "true" diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java new file mode 100644 index 000000000000..928b35736b5b --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java @@ -0,0 +1,1037 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * Copyright 2018 SmartBear Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import java.io.File; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.samskivert.mustache.Mustache; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.CliOption; +import org.openapitools.codegen.CodegenConstants; +import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenOperation; +import org.openapitools.codegen.CodegenParameter; +import org.openapitools.codegen.CodegenProperty; +import org.openapitools.codegen.CodegenType; +import org.openapitools.codegen.SupportingFile; +import org.openapitools.codegen.meta.features.ClientModificationFeature; +import org.openapitools.codegen.meta.features.DocumentationFeature; +import org.openapitools.codegen.meta.features.GlobalFeature; +import org.openapitools.codegen.meta.features.ParameterFeature; +import org.openapitools.codegen.meta.features.SchemaSupportFeature; +import org.openapitools.codegen.meta.features.SecurityFeature; +import org.openapitools.codegen.meta.features.WireFormatFeature; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.ModelsMap; +import org.openapitools.codegen.model.OperationMap; +import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.ProcessUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.util.Collections.sort; + +public class TidalKotlinClientCodegen extends AbstractKotlinCodegen { + + private final Logger LOGGER = LoggerFactory.getLogger(TidalKotlinClientCodegen.class); + + protected static final String JVM = "jvm"; + protected static final String JVM_KTOR = "jvm-ktor"; + protected static final String JVM_OKHTTP = "jvm-okhttp"; + protected static final String JVM_OKHTTP4 = "jvm-okhttp4"; + protected static final String JVM_RETROFIT2 = "jvm-retrofit2"; + protected static final String MULTIPLATFORM = "multiplatform"; + protected static final String JVM_VOLLEY = "jvm-volley"; + protected static final String JVM_VERTX = "jvm-vertx"; + protected static final String JVM_SPRING = "jvm-spring"; + protected static final String JVM_SPRING_WEBCLIENT = "jvm-spring-webclient"; + protected static final String JVM_SPRING_RESTCLIENT = "jvm-spring-restclient"; + + public static final String USE_RX_JAVA3 = "useRxJava3"; + public static final String USE_COROUTINES = "useCoroutines"; + public static final String DO_NOT_USE_RX_AND_COROUTINES = "doNotUseRxAndCoroutines"; + public static final String GENERATE_ROOM_MODELS = "generateRoomModels"; + public static final String ROOM_MODEL_PACKAGE = "roomModelPackage"; + public static final String OMIT_GRADLE_PLUGIN_VERSIONS = "omitGradlePluginVersions"; + public static final String OMIT_GRADLE_WRAPPER = "omitGradleWrapper"; + public static final String USE_SETTINGS_GRADLE = "useSettingsGradle"; + public static final String IDEA = "idea"; + public static final String USE_SPRING_BOOT3 = "useSpringBoot3"; + + public static final String DATE_LIBRARY = "dateLibrary"; + public static final String REQUEST_DATE_CONVERTER = "requestDateConverter"; + public static final String COLLECTION_TYPE = "collectionType"; + public static final String FAIL_ON_UNKNOWN_PROPERTIES = "failOnUnknownProperties"; + + public static final String MOSHI_CODE_GEN = "moshiCodeGen"; + + public static final String NULLABLE_RETURN_TYPE = "nullableReturnType"; + + public static final String SUPPORT_ANDROID_API_LEVEL_25_AND_BELLOW = "supportAndroidApiLevel25AndBelow"; + + public static final String GENERATE_ONEOF_ANYOF_WRAPPERS = "generateOneOfAnyOfWrappers"; + + protected static final String VENDOR_EXTENSION_BASE_NAME_LITERAL = "x-base-name-literal"; + + @Setter protected String dateLibrary = DateLibrary.JAVA8.value; + @Setter protected String requestDateConverter = RequestDateConverter.TO_JSON.value; + @Setter protected String collectionType = CollectionType.LIST.value; + protected boolean useRxJava3 = false; + protected boolean useCoroutines = false; + // backwards compatibility for openapi configs that specify neither rx1 nor rx2 + // (mustache does not allow for boolean operators so we need this extra field) + protected boolean doNotUseRxAndCoroutines = true; + protected boolean generateRoomModels = false; + @Setter protected String roomModelPackage = ""; + @Setter protected boolean omitGradleWrapper = false; + @Setter protected boolean generateOneOfAnyOfWrappers = true; + @Getter @Setter protected boolean failOnUnknownProperties = false; + + protected String authFolder; + + @Getter protected SERIALIZATION_LIBRARY_TYPE serializationLibrary = SERIALIZATION_LIBRARY_TYPE.moshi; + public static final String SERIALIZATION_LIBRARY_DESC = "What serialization library to use: 'moshi' (default), or 'gson' or 'jackson' or 'kotlinx_serialization'"; + + public enum SERIALIZATION_LIBRARY_TYPE {moshi, gson, jackson, kotlinx_serialization} + + public enum DateLibrary { + STRING("string"), + THREETENBP("threetenbp"), + THREETENBP_LOCALDATETIME("threetenbp-localdatetime"), + JAVA8("java8"), + JAVA8_LOCALDATETIME("java8-localdatetime"), + KOTLINX_DATETIME("kotlinx-datetime"); + + public final String value; + + DateLibrary(String value) { + this.value = value; + } + } + + public enum RequestDateConverter { + TO_STRING("toString"), + TO_JSON("toJson"); + + public final String value; + + RequestDateConverter(String value) { + this.value = value; + } + } + + public enum CollectionType { + ARRAY("array"), + LIST("list"); + + public final String value; + + CollectionType(String value) { + this.value = value; + } + } + + /** + * Constructs an instance of `TidalKotlinClientCodegen`. + */ + public TidalKotlinClientCodegen() { + super(); + + /* + * OAuth flows supported _only_ by client explicitly setting bearer token. The "flows" are not supported. + */ + modifyFeatureSet(features -> features + .includeDocumentationFeatures(DocumentationFeature.Readme) + .excludeWireFormatFeatures(WireFormatFeature.XML, WireFormatFeature.PROTOBUF) + .securityFeatures(EnumSet.of( + SecurityFeature.BasicAuth, + SecurityFeature.ApiKey, + SecurityFeature.BearerToken, + SecurityFeature.OAuth2_AuthorizationCode,//retrofit only + SecurityFeature.OAuth2_Implicit)) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .excludeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .excludeParameterFeatures( + ParameterFeature.Cookie + ) + .includeClientModificationFeatures(ClientModificationFeature.BasePath) + ); + + artifactId = "tidal-kotlin-client"; + packageName = "org.openapitools.client"; + + // cliOptions default redefinition need to be updated + updateOption(CodegenConstants.ARTIFACT_ID, this.artifactId); + updateOption(CodegenConstants.PACKAGE_NAME, this.packageName); + + outputFolder = "generated-code" + File.separator + "kotlin-client"; + modelTemplateFiles.put("model.mustache", ".kt"); + if (generateRoomModels) { + modelTemplateFiles.put("model_room.mustache", ".kt"); + } + apiTemplateFiles.put("api.mustache", ".kt"); + apiTestTemplateFiles.put("api_test.mustache", ".kt"); + modelTestTemplateFiles.put("model_test.mustache", ".kt"); + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + embeddedTemplateDir = templateDir = "kotlin-client"; + apiPackage = packageName + ".apis"; + modelPackage = packageName + ".models"; + + CliOption dateLibrary = new CliOption(DATE_LIBRARY, "Option. Date library to use"); + Map dateOptions = new HashMap<>(); + dateOptions.put(DateLibrary.THREETENBP.value, "Threetenbp - Backport of JSR310 (jvm only, preferred for jdk < 1.8)"); + dateOptions.put(DateLibrary.THREETENBP_LOCALDATETIME.value, "Threetenbp - Backport of JSR310 (jvm only, for legacy app only)"); + dateOptions.put(DateLibrary.STRING.value, "String"); + dateOptions.put(DateLibrary.JAVA8.value, "Java 8 native JSR310 (jvm only, preferred for jdk 1.8+)"); + dateOptions.put(DateLibrary.JAVA8_LOCALDATETIME.value, "Java 8 native JSR310 (jvm only, for legacy app only)"); + dateOptions.put(DateLibrary.KOTLINX_DATETIME.value, "kotlinx-datetime (preferred for multiplatform)"); + dateLibrary.setEnum(dateOptions); + dateLibrary.setDefault(this.dateLibrary); + cliOptions.add(dateLibrary); + + CliOption collectionType = new CliOption(COLLECTION_TYPE, "Option. Collection type to use"); + Map collectionOptions = new HashMap<>(); + collectionOptions.put(CollectionType.ARRAY.value, "kotlin.Array"); + collectionOptions.put(CollectionType.LIST.value, "kotlin.collections.List"); + collectionType.setEnum(collectionOptions); + collectionType.setDefault(this.collectionType); + cliOptions.add(collectionType); + + supportedLibraries.put(JVM_KTOR, "Platform: Java Virtual Machine. HTTP client: Ktor 1.6.7. JSON processing: Gson, Jackson (default)."); + supportedLibraries.put(JVM_OKHTTP4, "[DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0."); + supportedLibraries.put(JVM_SPRING_WEBCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 5 (or 6 with useSpringBoot3 enabled) WebClient. JSON processing: Jackson."); + supportedLibraries.put(JVM_SPRING_RESTCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 6 RestClient. JSON processing: Jackson."); + supportedLibraries.put(JVM_RETROFIT2, "Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2."); + supportedLibraries.put(MULTIPLATFORM, "Platform: Kotlin multiplatform. HTTP client: Ktor 1.6.7. JSON processing: Kotlinx Serialization: 1.2.1."); + supportedLibraries.put(JVM_VOLLEY, "Platform: JVM for Android. HTTP client: Volley 1.2.1. JSON processing: gson 2.8.9"); + supportedLibraries.put(JVM_VERTX, "Platform: Java Virtual Machine. HTTP client: Vert.x Web Client. JSON processing: Moshi, Gson or Jackson."); + + CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "Library template (sub-template) to use"); + libraryOption.setEnum(supportedLibraries); + libraryOption.setDefault(JVM_OKHTTP4); + cliOptions.add(libraryOption); + setLibrary(JVM_OKHTTP4); + + CliOption requestDateConverter = new CliOption(REQUEST_DATE_CONVERTER, "JVM-Option. Defines in how to handle date-time objects that are used for a request (as query or parameter)"); + Map requestDateConverterOptions = new HashMap<>(); + requestDateConverterOptions.put(RequestDateConverter.TO_JSON.value, "[DEFAULT] Date formatter option using a json converter."); + requestDateConverterOptions.put(RequestDateConverter.TO_STRING.value, "Use the 'toString'-method of the date-time object to retrieve the related string representation."); + requestDateConverter.setEnum(requestDateConverterOptions); + requestDateConverter.setDefault(this.requestDateConverter); + cliOptions.add(requestDateConverter); + + cliOptions.add(CliOption.newBoolean(USE_RX_JAVA3, "Whether to use the RxJava3 adapter with the retrofit2 library.")); + cliOptions.add(CliOption.newBoolean(USE_COROUTINES, "Whether to use the Coroutines adapter with the retrofit2 library.")); + cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT3, "Whether to use the Spring Boot 3 with the jvm-spring-webclient library.")); + cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_PLUGIN_VERSIONS, "Whether to declare Gradle plugin versions in build files.")); + cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_WRAPPER, "Whether to omit Gradle wrapper for creating a sub project.")); + cliOptions.add(CliOption.newBoolean(USE_SETTINGS_GRADLE, "Whether the project uses settings.gradle.")); + cliOptions.add(CliOption.newBoolean(IDEA, "Add IntellJ Idea plugin and mark Kotlin main and test folders as source folders.")); + + cliOptions.add(CliOption.newBoolean(MOSHI_CODE_GEN, "Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info.")); + cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", false)); + + cliOptions.add(CliOption.newBoolean(NULLABLE_RETURN_TYPE, "Nullable return type")); + + cliOptions.add(CliOption.newBoolean(GENERATE_ROOM_MODELS, "Generate Android Room database models in addition to API models (JVM Volley library only)", false)); + + cliOptions.add(CliOption.newBoolean(SUPPORT_ANDROID_API_LEVEL_25_AND_BELLOW, "[WARNING] This flag will generate code that has a known security vulnerability. It uses `kotlin.io.createTempFile` instead of `java.nio.file.Files.createTempFile` in order to support Android API level 25 and bellow. For more info, please check the following links https://github.com/OpenAPITools/openapi-generator/security/advisories/GHSA-23x4-m842-fmwf, https://github.com/OpenAPITools/openapi-generator/pull/9284")); + + cliOptions.add(CliOption.newBoolean(GENERATE_ONEOF_ANYOF_WRAPPERS, "Generate oneOf, anyOf schemas as wrappers.")); + + CliOption serializationLibraryOpt = new CliOption(CodegenConstants.SERIALIZATION_LIBRARY, SERIALIZATION_LIBRARY_DESC); + cliOptions.add(serializationLibraryOpt.defaultValue(serializationLibrary.name())); + } + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "kotlin"; + } + + @Override + public String getHelp() { + return "Generates a Kotlin client."; + } + + public boolean getGenerateRoomModels() { + return generateRoomModels; + } + + public boolean getOmitGradleWrapper() { + return omitGradleWrapper; + } + + public boolean getGenerateOneOfAnyOfWrappers() { + return generateOneOfAnyOfWrappers; + } + + public void setGenerateRoomModels(Boolean generateRoomModels) { + this.generateRoomModels = generateRoomModels; + } + + public void setUseRxJava3(boolean useRxJava3) { + if (useRxJava3) { + this.doNotUseRxAndCoroutines = false; + this.useCoroutines = false; + } + this.useRxJava3 = useRxJava3; + } + + public void setDoNotUseRxAndCoroutines(boolean doNotUseRxAndCoroutines) { + if (doNotUseRxAndCoroutines) { + this.useRxJava3 = false; + this.useCoroutines = false; + } + this.doNotUseRxAndCoroutines = doNotUseRxAndCoroutines; + } + + public void setUseCoroutines(boolean useCoroutines) { + if (useCoroutines) { + this.useRxJava3 = false; + this.doNotUseRxAndCoroutines = false; + } + this.useCoroutines = useCoroutines; + } + + /** + * Sets the serialization engine for Kotlin + * + * @param enumSerializationLibrary The string representation of the serialization library as defined by + * {@link org.openapitools.codegen.languages.TidalKotlinClientCodegen.SERIALIZATION_LIBRARY_TYPE} + */ + public void setSerializationLibrary(final String enumSerializationLibrary) { + try { + this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.valueOf(enumSerializationLibrary); + } catch (IllegalArgumentException ex) { + StringBuilder sb = new StringBuilder(enumSerializationLibrary + " is an invalid enum property naming option. Please choose from:"); + for (SERIALIZATION_LIBRARY_TYPE t : SERIALIZATION_LIBRARY_TYPE.values()) { + sb.append("\n ").append(t.name()); + } + throw new RuntimeException(sb.toString()); + } + } + + @Override + public String modelFilename(String templateName, String modelName) { + String suffix = modelTemplateFiles().get(templateName); + // If this was a proper template method, i wouldn't have to make myself throw up by doing this.... + if (getGenerateRoomModels() && suffix.startsWith("RoomModel")) { + return roomModelFileFolder() + File.separator + toModelFilename(modelName) + suffix; + } else { + return modelFileFolder() + File.separator + toModelFilename(modelName) + suffix; + } + } + + public String roomModelFileFolder() { + return outputFolder + File.separator + sourceFolder + File.separator + roomModelPackage.replace('.', File.separatorChar); + } + + @Override + public void processOpts() { + if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { + setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); + } else { + // Set the value to defaults if we haven't overridden + if (MULTIPLATFORM.equals(getLibrary())) { + setSourceFolder("src/commonMain/kotlin"); + } else if (JVM_VOLLEY.equals(getLibrary())) { + // Android plugin wants it's source in java + setSourceFolder("src/main/java"); + } else { + setSourceFolder(super.sourceFolder); + } + additionalProperties.put(CodegenConstants.SOURCE_FOLDER, this.sourceFolder); + } + + super.processOpts(); + + boolean hasRx3 = additionalProperties.containsKey(USE_RX_JAVA3); + boolean hasCoroutines = additionalProperties.containsKey(USE_COROUTINES); + int optionCount = 0; + if (hasRx3) { + optionCount++; + } + if (hasCoroutines) { + optionCount++; + } + boolean hasConflict = optionCount > 1; + + // RxJava & Coroutines + if (hasConflict) { + LOGGER.warn("You specified RxJava versions 1 and 2 and 3 or Coroutines together, please choose one of them."); + } else if (hasRx3) { + this.setUseRxJava3(Boolean.parseBoolean(additionalProperties.get(USE_RX_JAVA3).toString())); + } else if (hasCoroutines) { + this.setUseCoroutines(Boolean.parseBoolean(additionalProperties.get(USE_COROUTINES).toString())); + } + + if (!hasRx3 && !hasCoroutines) { + setDoNotUseRxAndCoroutines(true); + additionalProperties.put(DO_NOT_USE_RX_AND_COROUTINES, true); + } + + // infrastructure destination folder + final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", "/"); + authFolder = (sourceFolder + File.separator + packageName + File.separator + "auth").replace(".", "/"); + + // request destination folder + final String requestFolder = (sourceFolder + File.separator + packageName + File.separator + "request").replace(".", "/"); + + // auth destination folder + final String authFolder = (sourceFolder + File.separator + packageName + File.separator + "auth").replace(".", "/"); + + // additional properties + if (additionalProperties.containsKey(DATE_LIBRARY)) { + setDateLibrary(additionalProperties.get(DATE_LIBRARY).toString()); + } + + if (additionalProperties.containsKey(REQUEST_DATE_CONVERTER)) { + setRequestDateConverter(additionalProperties.get(REQUEST_DATE_CONVERTER).toString()); + } + + if (additionalProperties.containsKey(OMIT_GRADLE_WRAPPER)) { + setOmitGradleWrapper(Boolean.parseBoolean(additionalProperties.get(OMIT_GRADLE_WRAPPER).toString())); + } + + if (additionalProperties.containsKey(CodegenConstants.SERIALIZATION_LIBRARY)) { + setSerializationLibrary((String) additionalProperties.get(CodegenConstants.SERIALIZATION_LIBRARY)); + additionalProperties.put(this.serializationLibrary.name(), true); + } else { + additionalProperties.put(this.serializationLibrary.name(), true); + } + + if (additionalProperties.containsKey(GENERATE_ONEOF_ANYOF_WRAPPERS)) { + setGenerateOneOfAnyOfWrappers(Boolean.parseBoolean(additionalProperties.get(GENERATE_ONEOF_ANYOF_WRAPPERS).toString())); + } + + if (additionalProperties.containsKey(FAIL_ON_UNKNOWN_PROPERTIES)) { + setFailOnUnknownProperties(Boolean.parseBoolean(additionalProperties.get(FAIL_ON_UNKNOWN_PROPERTIES).toString())); + } else { + additionalProperties.put(FAIL_ON_UNKNOWN_PROPERTIES, false); + setFailOnUnknownProperties(false); + } + + commonSupportingFiles(); + + switch (getLibrary()) { + case JVM_KTOR: + processJVMKtorLibrary(infrastructureFolder); + break; + case JVM_OKHTTP4: + processJVMOkHttpLibrary(infrastructureFolder); + break; + case JVM_VOLLEY: + processJVMVolleyLibrary(infrastructureFolder, requestFolder, authFolder); + break; + case JVM_RETROFIT2: + processJVMRetrofit2Library(infrastructureFolder); + break; + case JVM_SPRING_WEBCLIENT: + processJvmSpringWebClientLibrary(infrastructureFolder); + break; + case JVM_SPRING_RESTCLIENT: + processJvmSpringRestClientLibrary(infrastructureFolder); + break; + case MULTIPLATFORM: + processMultiplatformLibrary(infrastructureFolder); + break; + case JVM_VERTX: + processJVMVertXLibrary(infrastructureFolder); + break; + default: + break; + } + + processDateLibrary(); + processRequestDateConverter(); + + if (additionalProperties.containsKey(COLLECTION_TYPE)) { + setCollectionType(additionalProperties.get(COLLECTION_TYPE).toString()); + } + + if (CollectionType.LIST.value.equals(collectionType)) { + if (isModelMutable()) { + typeMapping.put("array", "kotlin.collections.MutableList"); + typeMapping.put("list", "kotlin.collections.MutableList"); + } else { + typeMapping.put("array", "kotlin.collections.List"); + typeMapping.put("list", "kotlin.collections.List"); + } + additionalProperties.put("isList", true); + } + + if (usesRetrofit2Library()) { + boolean hasOAuthMethods = ProcessUtils.hasOAuthMethods(openAPI); + + if (hasOAuthMethods) { + supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); + supportingFiles.add(new SupportingFile("auth/OAuthFlow.kt.mustache", authFolder, "OAuthFlow.kt")); + supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.kt.mustache", authFolder, "OAuthOkHttpClient.kt")); + } + + if (hasOAuthMethods || ProcessUtils.hasApiKeyMethods(openAPI)) { + supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); + } + + if (ProcessUtils.hasHttpBearerMethods(openAPI)) { + supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); + } + + if (ProcessUtils.hasHttpBasicMethods(openAPI)) { + supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); + } + } + + additionalProperties.put("sanitizeGeneric", (Mustache.Lambda) (fragment, writer) -> { + String content = fragment.execute(); + for (final String s : List.of("<", ">", ",", " ", ".")) { + content = content.replace(s, ""); + } + writer.write(content); + }); + } + + private void processDateLibrary() { + if (DateLibrary.THREETENBP.value.equals(dateLibrary) || DateLibrary.THREETENBP_LOCALDATETIME.value.equals(dateLibrary)) { + processThreeTenBpDate(dateLibrary); + } else if (DateLibrary.STRING.value.equals(dateLibrary)) { + processStringDate(); + } else if (DateLibrary.JAVA8.value.equals(dateLibrary) || DateLibrary.JAVA8_LOCALDATETIME.value.equals(dateLibrary)) { + processJava8Date(dateLibrary); + } else if (DateLibrary.KOTLINX_DATETIME.value.equals(dateLibrary)) { + processKotlinxDate(); + } + } + + private void processRequestDateConverter() { + if (RequestDateConverter.TO_JSON.value.equals(requestDateConverter)) { + additionalProperties.put(RequestDateConverter.TO_JSON.value, true); + } else if (RequestDateConverter.TO_STRING.value.equals(requestDateConverter)) { + additionalProperties.put(RequestDateConverter.TO_STRING.value, true); + } + } + + private void processThreeTenBpDate(String dateLibrary) { + additionalProperties.put(DateLibrary.THREETENBP.value, true); + typeMapping.put("date", "LocalDate"); + importMapping.put("LocalDate", "org.threeten.bp.LocalDate"); + defaultIncludes.add("org.threeten.bp.LocalDate"); + + if (dateLibrary.equals(DateLibrary.THREETENBP.value)) { + typeMapping.put("date-time", "org.threeten.bp.OffsetDateTime"); + typeMapping.put("DateTime", "OffsetDateTime"); + importMapping.put("OffsetDateTime", "org.threeten.bp.OffsetDateTime"); + defaultIncludes.add("org.threeten.bp.OffsetDateTime"); + } else if (dateLibrary.equals(DateLibrary.THREETENBP_LOCALDATETIME.value)) { + typeMapping.put("date-time", "org.threeten.bp.LocalDateTime"); + typeMapping.put("DateTime", "LocalDateTime"); + importMapping.put("LocalDateTime", "org.threeten.bp.LocalDateTime"); + defaultIncludes.add("org.threeten.bp.LocalDateTime"); + } + } + + private void processStringDate() { + typeMapping.put("date-time", "kotlin.String"); + typeMapping.put("date", "kotlin.String"); + typeMapping.put("Date", "kotlin.String"); + typeMapping.put("DateTime", "kotlin.String"); + } + + private void processJava8Date(String dateLibrary) { + additionalProperties.put(DateLibrary.JAVA8.value, true); + + if (dateLibrary.equals(DateLibrary.JAVA8.value)) { + typeMapping.put("date-time", "java.time.OffsetDateTime"); + typeMapping.put("DateTime", "OffsetDateTime"); + importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); + } else if (dateLibrary.equals(DateLibrary.JAVA8_LOCALDATETIME.value)) { + typeMapping.put("date-time", "java.time.LocalDateTime"); + typeMapping.put("DateTime", "LocalDateTime"); + importMapping.put("LocalDateTime", "java.time.LocalDateTime"); + } + } + + private void processKotlinxDate() { + additionalProperties.put(DateLibrary.KOTLINX_DATETIME.value, true); + + typeMapping.put("date-time", "Instant"); + typeMapping.put("date", "LocalDate"); + typeMapping.put("time", "LocalTime"); + + typeMapping.put("DateTime", "Instant"); + typeMapping.put("Date", "LocalDate"); + typeMapping.put("Time", "LocalTime"); + + importMapping.put("Instant", "kotlinx.datetime.Instant"); + importMapping.put("LocalDate", "kotlinx.datetime.LocalDate"); + importMapping.put("LocalTime", "kotlinx.datetime.LocalTime"); + } + + private void processJVMRetrofit2Library(String infrastructureFolder) { + additionalProperties.put(JVM, true); + additionalProperties.put(JVM_RETROFIT2, true); + supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ResponseExt.kt.mustache", infrastructureFolder, "ResponseExt.kt")); + supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); + addSupportingSerializerAdapters(infrastructureFolder); + } + + private void processJVMVolleyLibrary(String infrastructureFolder, String requestFolder, String authFolder) { + + additionalProperties.put(JVM, true); + additionalProperties.put(JVM_VOLLEY, true); + + if (additionalProperties.containsKey(GENERATE_ROOM_MODELS)) { + this.setGenerateRoomModels(convertPropertyToBooleanAndWriteBack(GENERATE_ROOM_MODELS)); + // Hide this option behind a property getter and setter in case we need to check it elsewhere + if (getGenerateRoomModels()) { + modelTemplateFiles.put("model_room.mustache", "RoomModel.kt"); + supportingFiles.add(new SupportingFile("infrastructure/ITransformForStorage.mustache", infrastructureFolder, "ITransformForStorage.kt")); + + } + } else { + additionalProperties.put(GENERATE_ROOM_MODELS, generateRoomModels); + } + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + if (!additionalProperties.containsKey(ROOM_MODEL_PACKAGE)) + this.setRoomModelPackage(packageName + ".models.room"); + else + this.setRoomModelPackage(additionalProperties.get(ROOM_MODEL_PACKAGE).toString()); + } + additionalProperties.put(ROOM_MODEL_PACKAGE, roomModelPackage); + + supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); + + // We have auth related partial files, so they can be overridden, but don't generate them explicitly + supportingFiles.add(new SupportingFile("request/GsonRequest.mustache", requestFolder, "GsonRequest.kt")); + supportingFiles.add(new SupportingFile("request/IRequestFactory.mustache", requestFolder, "IRequestFactory.kt")); + supportingFiles.add(new SupportingFile("request/RequestFactory.mustache", requestFolder, "RequestFactory.kt")); + supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); + + if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.gson) { + throw new RuntimeException("This library currently only supports gson serialization. Try adding '--additional-properties serializationLibrary=gson' to your command."); + } + addSupportingSerializerAdapters(infrastructureFolder); + supportingFiles.remove(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); + + } + + private void addSupportingSerializerAdapters(final String infrastructureFolder) { + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); + + switch (getSerializationLibrary()) { + case moshi: + if (enumUnknownDefaultCase) { + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/SerializerHelper.kt.mustache", infrastructureFolder, "SerializerHelper.kt")); + } + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); + addKotlinxDateTimeAdapters(infrastructureFolder); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); + break; + + case gson: + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); + addKotlinxDateTimeAdapters(infrastructureFolder); + break; + + case jackson: + break; + + case kotlinx_serialization: + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache", infrastructureFolder, "AtomicBooleanAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache", infrastructureFolder, "AtomicIntegerAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicLongAdapter.kt.mustache", infrastructureFolder, "AtomicLongAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URLAdapter.kt.mustache", infrastructureFolder, "URLAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/StringBuilderAdapter.kt.mustache", infrastructureFolder, "StringBuilderAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/proguard-rules.pro.mustache", "", "proguard-rules.pro")); + break; + } + } + + private void addKotlinxDateTimeAdapters(final String infrastructureFolder) { + if (DateLibrary.KOTLINX_DATETIME.value.equals(dateLibrary)) { + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/InstantAdapter.kt.mustache", infrastructureFolder, "InstantAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalTimeAdapter.kt.mustache", infrastructureFolder, "LocalTimeAdapter.kt")); + } + } + + private void processJVMKtorLibrary(final String infrastructureFolder) { + // in future kotlinx.serialization may be added + if (this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.gson && this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.jackson) { + this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.jackson; + } + + additionalProperties.put(JVM, true); + additionalProperties.put(JVM_KTOR, true); + + defaultIncludes.add("io.ktor.client.request.forms.InputProvider"); + + importMapping.put("InputProvider", "io.ktor.client.request.forms.InputProvider"); + + supportingFiles.add(new SupportingFile("infrastructure/ApiAbstractions.kt.mustache", infrastructureFolder, "ApiAbstractions.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); + supportingFiles.add(new SupportingFile("infrastructure/HttpResponse.kt.mustache", infrastructureFolder, "HttpResponse.kt")); + supportingFiles.add(new SupportingFile("infrastructure/RequestConfig.kt.mustache", infrastructureFolder, "RequestConfig.kt")); + supportingFiles.add(new SupportingFile("infrastructure/RequestMethod.kt.mustache", infrastructureFolder, "RequestMethod.kt")); + + supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); + supportingFiles.add(new SupportingFile("auth/Authentication.kt.mustache", authFolder, "Authentication.kt")); + supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); + supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); + supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); + } + + /** + * Process Vert.x client options + * + * @param infrastructureFolder infrastructure destination folder + */ + private void processJVMVertXLibrary(final String infrastructureFolder) { + supportingFiles.add(new SupportingFile("infrastructure/ApiAbstractions.kt.mustache", infrastructureFolder, "ApiAbstractions.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); + supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApiResponse.kt.mustache", infrastructureFolder, "ApiResponse.kt")); + addSupportingSerializerAdapters(infrastructureFolder); + + additionalProperties.put(JVM, true); + additionalProperties.put(JVM_VERTX, true); + } + + private void processJVMOkHttpLibrary(final String infrastructureFolder) { + commonJvmMultiplatformSupportingFiles(infrastructureFolder); + addSupportingSerializerAdapters(infrastructureFolder); + + additionalProperties.put(JVM, true); + additionalProperties.put(JVM_OKHTTP, true); + + if (JVM_OKHTTP4.equals(getLibrary())) { + additionalProperties.put(JVM_OKHTTP4, true); + } + + supportedLibraries.put(JVM_OKHTTP, "A workaround to use the same template folder for both 'jvm-okhttp3' and 'jvm-okhttp4'."); + setLibrary(JVM_OKHTTP); + + // jvm specific supporting files + supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ResponseExtensions.kt.mustache", infrastructureFolder, "ResponseExtensions.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApiResponse.kt.mustache", infrastructureFolder, "ApiResponse.kt")); + } + + private void proccessJvmSpring(final String infrastructureFolder) { + if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.jackson) { + throw new RuntimeException("This library currently only supports jackson serialization. Try adding '--additional-properties serializationLibrary=jackson' to your command."); + } + + commonJvmMultiplatformSupportingFiles(infrastructureFolder); + addSupportingSerializerAdapters(infrastructureFolder); + + additionalProperties.put(JVM_SPRING, true); + additionalProperties.put(JVM, true); + } + + private void processJvmSpringWebClientLibrary(final String infrastructureFolder) { + proccessJvmSpring(infrastructureFolder); + additionalProperties.put(JVM_SPRING_WEBCLIENT, true); + } + + private void processJvmSpringRestClientLibrary(final String infrastructureFolder) { + if (additionalProperties.getOrDefault(USE_SPRING_BOOT3, false).equals(false)) { + throw new RuntimeException("This library must use Spring Boot 3. Try adding '--additional-properties useSpringBoot3=true' to your command."); + } + + proccessJvmSpring(infrastructureFolder); + additionalProperties.put(JVM_SPRING_RESTCLIENT, true); + } + + private void processMultiplatformLibrary(final String infrastructureFolder) { + commonJvmMultiplatformSupportingFiles(infrastructureFolder); + additionalProperties.put(MULTIPLATFORM, true); + + if (!DateLibrary.STRING.value.equals(dateLibrary) && !DateLibrary.KOTLINX_DATETIME.value.equals(dateLibrary)) { + throw new RuntimeException("Multiplatform only supports string and kotlinx-datetime. Try adding '--additional-properties dateLibrary=kotlinx-datetime' to your command."); + } + + setRequestDateConverter(RequestDateConverter.TO_STRING.value); + + // multiplatform default includes + defaultIncludes.add("io.ktor.client.request.forms.InputProvider"); + defaultIncludes.add(packageName + ".infrastructure.Base64ByteArray"); + defaultIncludes.add(packageName + ".infrastructure.OctetByteArray"); + + // multiplatform type mapping + typeMapping.put("number", "kotlin.Double"); + typeMapping.put("file", "OctetByteArray"); + typeMapping.put("binary", "OctetByteArray"); + typeMapping.put("ByteArray", "Base64ByteArray"); + typeMapping.put("object", "kotlin.String"); // kotlin.Any not serializable + + // multiplatform import mapping + importMapping.put("BigDecimal", "kotlin.Double"); + importMapping.put("UUID", "kotlin.String"); + importMapping.put("URI", "kotlin.String"); + importMapping.put("InputProvider", "io.ktor.client.request.forms.InputProvider"); + importMapping.put("File", packageName + ".infrastructure.OctetByteArray"); + importMapping.put("Timestamp", "kotlin.String"); + importMapping.put("LocalDateTime", "kotlin.String"); + importMapping.put("LocalDate", "kotlin.String"); + importMapping.put("LocalTime", "kotlin.String"); + importMapping.put("Base64ByteArray", packageName + ".infrastructure.Base64ByteArray"); + importMapping.put("OctetByteArray", packageName + ".infrastructure.OctetByteArray"); + + // multiplatform specific supporting files + supportingFiles.add(new SupportingFile("infrastructure/Base64ByteArray.kt.mustache", infrastructureFolder, "Base64ByteArray.kt")); + supportingFiles.add(new SupportingFile("infrastructure/Bytes.kt.mustache", infrastructureFolder, "Bytes.kt")); + supportingFiles.add(new SupportingFile("infrastructure/HttpResponse.kt.mustache", infrastructureFolder, "HttpResponse.kt")); + supportingFiles.add(new SupportingFile("infrastructure/OctetByteArray.kt.mustache", infrastructureFolder, "OctetByteArray.kt")); + + // multiplatform specific auth + supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); + supportingFiles.add(new SupportingFile("auth/Authentication.kt.mustache", authFolder, "Authentication.kt")); + supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); + supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); + supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); + + // multiplatform specific testing files + supportingFiles.add(new SupportingFile("commonTest/Coroutine.kt.mustache", "src/commonTest/kotlin/util", "Coroutine.kt")); + supportingFiles.add(new SupportingFile("iosTest/Coroutine.kt.mustache", "src/iosTest/kotlin/util", "Coroutine.kt")); + supportingFiles.add(new SupportingFile("jsTest/Coroutine.kt.mustache", "src/jsTest/kotlin/util", "Coroutine.kt")); + supportingFiles.add(new SupportingFile("jvmTest/Coroutine.kt.mustache", "src/jvmTest/kotlin/util", "Coroutine.kt")); + } + + + private void commonJvmMultiplatformSupportingFiles(String infrastructureFolder) { + 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/PartConfig.kt.mustache", infrastructureFolder, "PartConfig.kt")); + supportingFiles.add(new SupportingFile("infrastructure/RequestConfig.kt.mustache", infrastructureFolder, "RequestConfig.kt")); + supportingFiles.add(new SupportingFile("infrastructure/RequestMethod.kt.mustache", infrastructureFolder, "RequestMethod.kt")); + } + + private void commonSupportingFiles() { + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + if (getLibrary().equals(MULTIPLATFORM)) { + supportingFiles.add(new SupportingFile("build.gradle.kts.mustache", "", "build.gradle.kts")); + supportingFiles.add(new SupportingFile("settings.gradle.kts.mustache", "", "settings.gradle.kts")); + } else if (getLibrary().equals(JVM_VOLLEY)) { + supportingFiles.add(new SupportingFile("build.mustache", "", "build.gradle")); + supportingFiles.add(new SupportingFile("gradle.properties.mustache", "", "gradle.properties")); + supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle")); + supportingFiles.add(new SupportingFile("manifest.mustache", "", "src/main/AndroidManifest.xml")); + } else { + supportingFiles.add(new SupportingFile("build.gradle.mustache", "", "build.gradle")); + supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle")); + } + + // gradle wrapper supporting files + if (!getOmitGradleWrapper()) { + supportingFiles.add(new SupportingFile("gradlew.mustache", "", "gradlew")); + supportingFiles.add(new SupportingFile("gradlew.bat.mustache", "", "gradlew.bat")); + supportingFiles.add(new SupportingFile("gradle-wrapper.properties.mustache", "gradle.wrapper".replace(".", File.separator), "gradle-wrapper.properties")); + supportingFiles.add(new SupportingFile("gradle-wrapper.jar", "gradle.wrapper".replace(".", File.separator), "gradle-wrapper.jar")); + } + } + + @Override + public ModelsMap postProcessModels(ModelsMap objs) { + ModelsMap objects = super.postProcessModels(objs); + + for (ModelMap mo : objects.getModels()) { + CodegenModel cm = mo.getModel(); + if (getGenerateRoomModels() || getGenerateOneOfAnyOfWrappers()) { + cm.vendorExtensions.put("x-has-data-class-body", true); + } + + // escape the variable base name for use as a string literal + List vars = Stream.of( + cm.vars, + cm.allVars, + cm.optionalVars, + cm.requiredVars, + cm.readOnlyVars, + cm.readWriteVars, + cm.parentVars + ) + .flatMap(List::stream) + .collect(Collectors.toList()); + + for (CodegenProperty var : vars) { + var.vendorExtensions.put(VENDOR_EXTENSION_BASE_NAME_LITERAL, var.baseName.replace("$", "\\$")); + } + } + + return objects; + } + + private boolean usesRetrofit2Library() { + return getLibrary() != null && getLibrary().contains(JVM_RETROFIT2); + } + + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { + super.postProcessOperationsWithModels(objs, allModels); + OperationMap operations = objs.getOperations(); + boolean isResponseFile = false; + if (operations != null) { + List ops = operations.getOperation(); + for (CodegenOperation operation : ops) { + isResponseFile = isResponseFile || operation.isResponseFile; + + if (JVM_RETROFIT2.equals(getLibrary()) && StringUtils.isNotEmpty(operation.path) && operation.path.startsWith("/")) { + operation.path = operation.path.substring(1); + } + + if (JVM_OKHTTP.equals(getLibrary()) || JVM_OKHTTP4.equals(getLibrary())) { + // Ideally we would do content negotiation to choose the best mediatype, but that would be a next step. + // For now we take the first mediatype we can parse and send that. + Predicate> isSerializable = typeMapping -> { + String mediaTypeValue = typeMapping.get("mediaType"); + if (mediaTypeValue == null) + return false; + // match on first part in mediaTypes like 'application/json; charset=utf-8' + int endIndex = mediaTypeValue.indexOf(';'); + String mediaType = (endIndex == -1 + ? mediaTypeValue + : mediaTypeValue.substring(0, endIndex) + ).trim(); + return "multipart/form-data".equals(mediaType) + || "application/x-www-form-urlencoded".equals(mediaType) + || (mediaType.startsWith("application/") && (mediaType.endsWith("json") || mediaType.endsWith("octet-stream"))); + }; + operation.consumes = operation.consumes == null ? null : operation.consumes.stream() + .filter(isSerializable) + .limit(1) + .collect(Collectors.toList()); + operation.hasConsumes = operation.consumes != null && !operation.consumes.isEmpty(); + + operation.produces = operation.produces == null ? null : operation.produces.stream() + .filter(isSerializable) + .collect(Collectors.toList()); + operation.hasProduces = operation.produces != null && !operation.produces.isEmpty(); + } + + // set multipart against all relevant operations + if (operation.hasConsumes == Boolean.TRUE) { + if (isMultipartType(operation.consumes)) { + operation.isMultipart = Boolean.TRUE; + } + } + + // import okhttp3.MultipartBody if any parameter is a file + for (CodegenParameter param : operation.allParams) { + if (Boolean.TRUE.equals(param.isFile)) { + operations.put("x-kotlin-multipart-import", true); + } + + if (param.isQueryParam && "form".equals(param.style) && param.isExplode && param.isModel) { + // query parameter (style: form, explode) referencing models need to import + // models defined in the properties of the models + operations.put("x-kotlin-import-models", true); + } + } + + if (usesRetrofit2Library() && StringUtils.isNotEmpty(operation.path) && operation.path.startsWith("/")) { + operation.path = operation.path.substring(1); + } + + // sorting operation parameters to make sure path params are parsed before query params + if (operation.allParams != null) { + sort(operation.allParams, (one, another) -> { + if (one.isPathParam && another.isQueryParam) { + return -1; + } + if (one.isQueryParam && another.isPathParam) { + return 1; + } + + return 0; + }); + } + + // modify the data type of binary form parameters to a more friendly type for ktor builds + if ((JVM_KTOR.equals(getLibrary()) || MULTIPLATFORM.equals(getLibrary())) && operation.allParams != null) { + for (CodegenParameter param : operation.allParams) { + if (param.dataFormat != null && param.dataFormat.equals("binary")) { + param.baseType = param.dataType = "io.ktor.client.request.forms.InputProvider"; + } + } + } + } + } + objs.put("isResponseFile", isResponseFile); + return objs; + } + + private static boolean isMultipartType(List> consumes) { + Map firstType = consumes.get(0); + if (firstType != null) { + return "multipart/form-data".equals(firstType.get("mediaType")); + } + return false; + } + + @Override + public void postProcess() { + System.out.println("################################################################################"); + System.out.println("# Thanks for using OpenAPI Generator. #"); + System.out.println("# Please consider donation to help us maintain this project \uD83D\uDE4F #"); + System.out.println("# https://opencollective.com/openapi_generator/donate #"); + System.out.println("# #"); + System.out.println("# This generator's contributed by Jim Schubert (https://github.com/jimschubert)#"); + System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #"); + System.out.println("################################################################################"); + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 2f8c6c6074c2..8c7e0d3eb4d5 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -153,3 +153,6 @@ org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen org.openapitools.codegen.languages.WsdlSchemaCodegen org.openapitools.codegen.languages.XojoClientCodegen org.openapitools.codegen.languages.ZapierClientCodegen +org.openapitools.codegen.languages.RustAxumServerCodegen + +org.openapitools.codegen.languages.TidalKotlinClientCodegen diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache new file mode 100644 index 000000000000..5d4f0e1cdf81 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache @@ -0,0 +1,113 @@ +# {{packageName}} - Kotlin client library for {{appName}} + +{{#appDescriptionWithNewLines}} +{{{.}}} +{{/appDescriptionWithNewLines}} + +## Overview +This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate an API client. + +- API version: {{appVersion}} +- Package version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Generator version: {{generatorVersion}} +- Build package: {{generatorClass}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +## Requires + +{{#jvm}} +* Kotlin 1.7.21 +* Gradle 7.5 +{{/jvm}} +{{#multiplatform}} +* Kotlin 1.5.10 +{{/multiplatform}} + +## Build + +{{#jvm}} +First, create the gradle wrapper script: + +``` +gradle wrapper +``` + +Then, run: + +{{/jvm}} +``` +./gradlew check assemble +``` + +This runs all tests and packages the library. + +## Features/Implementation Notes + +* Supports JSON inputs/outputs, File inputs, and Form inputs. +* Supports collection formats for query parameters: csv, tsv, ssv, pipes. +* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions. +{{#jvm}}* Implementation of ApiClient is intended to reduce method counts, specifically to benefit Android targets.{{/jvm}} + +{{#generateApiDocs}} + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +| Class | Method | HTTP request | Description | +| ------------ | ------------- | ------------- | ------------- | +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}| *{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{{summary}}} | +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +{{/generateApiDocs}} + +{{#generateModelDocs}} + +## Documentation for Models + +{{#modelPackage}} +{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} +{{/modelPackage}} +{{^modelPackage}} +No model defined in this package +{{/modelPackage}} +{{/generateModelDocs}} + + +## Documentation for Authorization + +{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} +{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasicBasic}}- **Type**: HTTP basic authentication +{{/isBasicBasic}} +{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} +{{/isBasicBearer}} +{{#isHttpSignature}}- **Type**: HTTP signature authentication +{{/isHttpSignature}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}}{{#infoEmail}} + +## Author + +{{{.}}} +{{/infoEmail}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache new file mode 100644 index 000000000000..f4871c02cc2e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache @@ -0,0 +1,2 @@ +{{#additionalModelTypeAnnotations}}{{{.}}} +{{/additionalModelTypeAnnotations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache new file mode 100644 index 000000000000..a745e7745f0c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache @@ -0,0 +1,241 @@ +{{^multiplatform}} +{{#gson}} +{{#generateOneOfAnyOfWrappers}} +import com.google.gson.Gson +import com.google.gson.JsonElement +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.annotations.JsonAdapter +{{/generateOneOfAnyOfWrappers}} +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +{{/moshi}} +{{#jackson}} +{{#enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +{{/enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonProperty +{{#discriminator}} +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo +{{/discriminator}} +{{/jackson}} +{{#kotlinx_serialization}} +import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} +import kotlinx.serialization.SerialName +import kotlinx.serialization.Contextual +{{#enumUnknownDefaultCase}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +{{/enumUnknownDefaultCase}} +{{#hasEnums}} +{{/hasEnums}} +{{/kotlinx_serialization}} +{{#parcelizeModels}} +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +{{/parcelizeModels}} +{{/multiplatform}} +{{#multiplatform}} +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +{{/multiplatform}} +{{#serializableModel}} +import java.io.Serializable +{{/serializableModel}} +{{#generateRoomModels}} +import {{roomModelPackage}}.{{classname}}RoomModel +import {{packageName}}.infrastructure.ITransformForStorage +{{/generateRoomModels}} +import java.io.IOException + +/** + * {{{description}}} + * + */ +{{#parcelizeModels}} +@Parcelize +{{/parcelizeModels}} +{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} +{{#isDeprecated}} +@Deprecated(message = "This schema is deprecated.") +{{/isDeprecated}} +{{>additionalModelTypeAnnotations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}(var actualInstance: Any? = null) { + + class CustomTypeAdapterFactory : TypeAdapterFactory { + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { + return null // this class only serializes '{{classname}}' and its subtypes + } + val elementAdapter = gson.getAdapter(JsonElement::class.java) + {{#composedSchemas}} + {{#anyOf}} + {{^isArray}} + {{^vendorExtensions.x-duplicated-data-type}} + val adapter{{{dataType}}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}::class.java)) + {{/vendorExtensions.x-duplicated-data-type}} + {{/isArray}} + {{#isArray}} + @Suppress("UNCHECKED_CAST") + val adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = gson.getDelegateAdapter(this, TypeToken.get(object : TypeToken<{{{dataType}}}>() {}.type)) as TypeAdapter<{{{dataType}}}> + {{/isArray}} + {{/anyOf}} + {{/composedSchemas}} + + @Suppress("UNCHECKED_CAST") + return object : TypeAdapter<{{classname}}?>() { + @Throws(IOException::class) + override fun write(out: JsonWriter,value: {{classname}}?) { + if (value?.actualInstance == null) { + elementAdapter.write(out, null) + return + } + + {{#composedSchemas}} + {{#anyOf}} + {{^vendorExtensions.x-duplicated-data-type}} + // check if the actual instance is of the type `{{{dataType}}}` + if (value.actualInstance is {{#isArray}}List<*>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) { + {{#isArray}} + val list = value.actualInstance as List + if (list.get(0) is {{{items.dataType}}}) { + val array = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonArray() + elementAdapter.write(out, array) + return + } + {{/isArray}} + {{^isArray}} + {{#isPrimitiveType}} + val primitive = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonPrimitive() + elementAdapter.write(out, primitive) + return + {{/isPrimitiveType}} + {{^isPrimitiveType}} + val element = adapter{{{dataType}}}.toJsonTree(value.actualInstance as {{{dataType}}}?) + elementAdapter.write(out, element) + return + {{/isPrimitiveType}} + {{/isArray}} + } + {{/vendorExtensions.x-duplicated-data-type}} + {{/anyOf}} + {{/composedSchemas}} + throw IOException("Failed to serialize as the type doesn't match anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}") + } + + @Throws(IOException::class) + override fun read(jsonReader: JsonReader): {{classname}} { + val jsonElement = elementAdapter.read(jsonReader) + val errorMessages = ArrayList() + var actualAdapter: TypeAdapter<*> + val ret = {{classname}}() + + {{#composedSchemas}} + {{#anyOf}} + {{^vendorExtensions.x-duplicated-data-type}} + {{^hasVars}} + // deserialize {{{dataType}}} + try { + // validate the JSON object to see if any exception is thrown + {{^isArray}} + {{#isNumber}} + require(jsonElement.getAsJsonPrimitive().isNumber()) { + String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) + } + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) + return ret + {{/isNumber}} + {{^isNumber}} + {{#isPrimitiveType}} + require(jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { + String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) + } + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) + return ret + {{/isPrimitiveType}} + {{/isNumber}} + {{^isNumber}} + {{^isPrimitiveType}} + {{{dataType}}}.validateJsonElement(jsonElement) + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) + return ret + {{/isPrimitiveType}} + {{/isNumber}} + {{/isArray}} + {{#isArray}} + require(jsonElement.isJsonArray) { + String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()) + } + + // validate array items + for(element in jsonElement.getAsJsonArray()) { + {{#items}} + {{#isNumber}} + require(jsonElement.getAsJsonPrimitive().isNumber) { + String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) + } + {{/isNumber}} + {{^isNumber}} + {{#isPrimitiveType}} + require(element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}) { + String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) + } + {{/isPrimitiveType}} + {{/isNumber}} + {{^isNumber}} + {{^isPrimitiveType}} + {{{dataType}}}.validateJsonElement(element) + {{/isPrimitiveType}} + {{/isNumber}} + {{/items}} + } + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) + return ret + {{/isArray}} + //log.log(Level.FINER, "Input data matches schema '{{{dataType}}}'") + } catch (e: Exception) { + // deserialization failed, continue + errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.message)) + //log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e) + } + {{/hasVars}} + {{#hasVars}} + // deserialize {{{.}}} + try { + // validate the JSON object to see if any exception is thrown + {{.}}.validateJsonElement(jsonElement) + log.log(Level.FINER, "Input data matches schema '{{{.}}}'") + actualAdapter = adapter{{.}} + ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) + return ret + } catch (e: Exception) { + // deserialization failed, continue + errorMessages.add(String.format("Deserialization for {{{.}}} failed with `%s`.", e.message)) + //log.log(Level.FINER, "Input data does not match schema '{{{.}}}'", e) + } + {{/hasVars}} + {{/vendorExtensions.x-duplicated-data-type}} + {{/anyOf}} + {{/composedSchemas}} + + throw IOException(String.format("Failed deserialization for {{classname}}: no schema match result. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString())) + } + }.nullSafe() as TypeAdapter + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache new file mode 100644 index 000000000000..16eafac78f43 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache @@ -0,0 +1,92 @@ +# {{classname}}{{#description}} +{{.}}{{/description}} + +All URIs are relative to *{{basePath}}* + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} | +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +# **{{operationId}}** +> {{#returnType}}{{.}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + +{{summary}}{{#notes}} + +{{.}}{{/notes}} + +### Example +```kotlin +// Import classes: +//import {{{packageName}}}.infrastructure.* +//import {{{modelPackage}}}.* + +{{! TODO: Auth method documentation examples}} +val apiInstance = {{{classname}}}() +{{#allParams}} +val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} +{{/allParams}} +try { + {{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}} + println(result){{/returnType}} +} catch (e: ClientException) { + println("4xx response calling {{{classname}}}#{{{operationId}}}") + e.printStackTrace() +} catch (e: ServerException) { + println("5xx response calling {{{classname}}}#{{{operationId}}}") + e.printStackTrace() +} +``` + +### Parameters +{{^allParams}} +This endpoint does not need any parameter. +{{/allParams}} +{{#allParams}} +{{#-last}} +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +{{/-last}} +| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} | +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}} +{{#authMethods}} +{{#isApiKey}} +Configure {{name}}: + ApiClient.apiKey["{{keyParamName}}"] = "" + ApiClient.apiKeyPrefix["{{keyParamName}}"] = "" +{{/isApiKey}} +{{#isBasic}} +{{#isBasicBasic}} +Configure {{name}}: + ApiClient.username = "" + ApiClient.password = "" +{{/isBasicBasic}} +{{#isBasicBearer}} +Configure {{name}}: + ApiClient.accessToken = "" +{{/isBasicBearer}} +{{/isBasic}} +{{#isOAuth}} +Configure {{name}}: + ApiClient.accessToken = "" +{{/isOAuth}} +{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +{{/operation}} +{{/operations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache new file mode 100644 index 000000000000..78ae85a01d03 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache @@ -0,0 +1,33 @@ +{{>licenseInfo}} +package {{apiPackage}} + +import io.kotlintest.shouldBe +import io.kotlintest.specs.ShouldSpec + +import {{apiPackage}}.{{{classname}}} +{{#imports}}import {{import}} +{{/imports}} + +{{#operations}} +class {{{classname}}}Test : ShouldSpec() { + init { + // uncomment below to create an instance of {{{classname}}} + //val apiInstance = {{{classname}}}() + + {{#operation}} + // to test {{{operationId}}}{{#description}} - {{{.}}}{{/description}} + should("test {{{operationId}}}") { + // uncomment below to test {{{operationId}}} + {{#allParams}} + //val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} + {{/allParams}} + //{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + {{#returnType}} + //result shouldBe ("TODO") + {{/returnType}} + } + + {{/operation}} + } +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache new file mode 100644 index 000000000000..1dfe6fd0636f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache @@ -0,0 +1,216 @@ +group '{{groupId}}' +version '{{artifactVersion}}' +{{^omitGradleWrapper}} + +wrapper { + gradleVersion = '8.7' + distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" +} +{{/omitGradleWrapper}} + +buildscript { + ext.kotlin_version = '1.9.23' + {{#jvm-ktor}} + ext.ktor_version = '2.3.9' + {{/jvm-ktor}} + {{#jvm-retrofit2}} + ext.retrofitVersion = '2.10.0' + {{/jvm-retrofit2}} + {{#useRxJava3}} + ext.rxJava3Version = '3.1.8' + {{/useRxJava3}} + {{#jvm-vertx}} + ext.vertx_version = "4.5.6" + {{/jvm-vertx}} + {{#jvm-spring}} + {{#useSpringBoot3}} + ext.spring_boot_version = "3.2.5" + {{/useSpringBoot3}} + {{^useSpringBoot3}} + ext.spring_boot_version = "2.7.18" + {{/useSpringBoot3}} + {{/jvm-spring}} + ext.spotless_version = "6.25.0" + + repositories { + maven { url "https://repo1.maven.org/maven2" } + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + {{#kotlinx_serialization}} + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + {{/kotlinx_serialization}} + classpath "com.diffplug.spotless:spotless-plugin-gradle:$spotless_version" + } +} + +apply plugin: 'kotlin' +{{#moshiCodeGen}} +apply plugin: 'kotlin-kapt' +{{/moshiCodeGen}} +{{#parcelizeModels}} +apply plugin: 'kotlin-parcelize' +{{/parcelizeModels}} +{{#kotlinx_serialization}} +apply plugin: 'kotlinx-serialization' +{{/kotlinx_serialization}} +{{#idea}} +apply plugin: 'idea' +{{/idea}} +apply plugin: 'maven-publish' +apply plugin: 'com.diffplug.spotless' +{{^useSettingsGradle}} + +repositories { + maven { url "https://repo1.maven.org/maven2" } +} +{{/useSettingsGradle}} + +// Use spotless plugin to automatically format code, remove unused import, etc +// To apply changes directly to the file, run `gradlew spotlessApply` +// Ref: https://github.com/diffplug/spotless/tree/main/plugin-gradle +spotless { + // comment out below to run spotless as part of the `check` task + enforceCheck false + + format 'misc', { + // define the files (e.g. '*.gradle', '*.md') to apply `misc` to + target '.gitignore' + + // define the steps to apply to those files + trimTrailingWhitespace() + indentWithSpaces() // Takes an integer argument if you don't like 4 + endWithNewline() + } + kotlin { + ktfmt() + } +} + +test { + useJUnitPlatform() +} +{{#idea}} + +idea { + module { + sourceDirs += file('src/main/kotlin') + testSourceDirs += file('src/test/kotlin') + } +} +{{/idea}} +{{#jvm-spring-webclient}}{{#useSpringBoot3}} +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} +{{/useSpringBoot3}}{{/jvm-spring-webclient}} +{{#jvm-spring-restclient}} +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} +{{/jvm-spring-restclient}} +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + {{^doNotUseRxAndCoroutines}} + {{#useCoroutines}} + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0" + {{/useCoroutines}} + {{/doNotUseRxAndCoroutines}} + {{#moshi}} + {{^moshiCodeGen}} + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + implementation "com.squareup.moshi:moshi-kotlin:1.15.1" + implementation "com.squareup.moshi:moshi-adapters:1.15.1" + {{/moshiCodeGen}} + {{#moshiCodeGen}} + implementation "com.squareup.moshi:moshi:1.15.1" + implementation "com.squareup.moshi:moshi-adapters:1.15.1" + kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.1" + {{/moshiCodeGen}} + {{/moshi}} + {{#gson}} + implementation "com.google.code.gson:gson:2.10.1" + {{/gson}} + {{#jackson}} + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.17.1" + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1" + {{/jackson}} + {{#kotlinx_serialization}} + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3" + {{/kotlinx_serialization}} + {{#jvm-ktor}} + implementation "io.ktor:ktor-client-core:$ktor_version" + implementation "io.ktor:ktor-client-content-negotiation:$ktor_version" + {{#gson}} + implementation "io.ktor:ktor-serialization-gson:$ktor_version" + implementation "io.ktor:ktor-client-gson:$ktor_version" + {{/gson}} + {{#jackson}} + implementation "io.ktor:ktor-client-jackson:$ktor_version" + implementation "io.ktor:ktor-serialization-jackson:$ktor_version" + {{/jackson}} + {{/jvm-ktor}} + {{#jvm-okhttp4}} + implementation "com.squareup.okhttp3:okhttp:4.12.0" + {{/jvm-okhttp4}} + {{#jvm-spring-webclient}} + implementation "org.springframework.boot:spring-boot-starter-webflux:$spring_boot_version" + implementation "io.projectreactor:reactor-core:3.6.5" + {{/jvm-spring-webclient}} + {{#jvm-spring-restclient}} + implementation "org.springframework.boot:spring-boot-starter-web:$spring_boot_version" + {{/jvm-spring-restclient}} + {{#threetenbp}} + implementation "org.threeten:threetenbp:1.6.8" + {{/threetenbp}} + {{#kotlinx-datetime}} + implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.5.0" + {{/kotlinx-datetime}} + {{#jvm-retrofit2}} + {{#hasOAuthMethods}} + implementation "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2" + {{/hasOAuthMethods}} + implementation "com.squareup.okhttp3:logging-interceptor:4.12.0" + {{#useRxJava3}} + implementation "io.reactivex.rxjava3:rxjava:$rxJava3Version" + implementation "com.squareup.retrofit2:adapter-rxjava3:2.10.0" + {{/useRxJava3}} + implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" + {{#gson}} + implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" + {{/gson}} + {{#moshi}} + implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion" + {{/moshi}} + {{#kotlinx_serialization}} + implementation "com.squareup.retrofit2:converter-kotlinx-serialization:$retrofitVersion" + {{/kotlinx_serialization}} + {{#jackson}} + implementation "com.squareup.retrofit2:converter-jackson:$retrofitVersion" + {{/jackson}} + implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion" + {{/jvm-retrofit2}} + testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2" + {{#jvm-vertx}} + implementation "io.vertx:vertx-web-client:$vertx_version" + implementation "io.vertx:vertx-core:$vertx_version" + implementation "io.vertx:vertx-lang-kotlin:$vertx_version" + implementation "io.vertx:vertx-uri-template:$vertx_version" + {{#useCoroutines}} + implementation "io.vertx:vertx-lang-kotlin-coroutines:$vertx_version" + {{/useCoroutines}} + {{/jvm-vertx}} +} +{{#kotlinx_serialization}} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + kotlinOptions { + freeCompilerArgs += "-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi" + } +} +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache new file mode 100644 index 000000000000..6ab282918f35 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache @@ -0,0 +1,15 @@ +# {{classname}} + +## Properties +| Name | Type | Description | Notes | +| ------------ | ------------- | ------------- | ------------- | +{{#vars}}| **{{name}}** | {{#isEnum}}[**inline**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | +{{/vars}} +{{#vars}}{{#isEnum}} + +{{!NOTE: see java's resources "pojo_doc.mustache" once enums are fully implemented}} +## Enum: {{baseName}} +| Name | Value | +| ---- | ----- |{{#allowableValues}} +| {{name}} | {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}} | +{{/isEnum}}{{/vars}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache new file mode 100644 index 000000000000..9290173fd6b1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache @@ -0,0 +1,363 @@ +{{^multiplatform}} +{{#gson}} +{{#generateOneOfAnyOfWrappers}} +import com.google.gson.Gson +import com.google.gson.JsonElement +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.annotations.JsonAdapter +import java.io.IOException +{{/generateOneOfAnyOfWrappers}} +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +{{/moshi}} +{{#jackson}} +{{#enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +{{/enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonProperty +{{#discriminator}} +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo +{{/discriminator}} +{{/jackson}} +{{#kotlinx_serialization}} +import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} +import kotlinx.serialization.SerialName +import kotlinx.serialization.Contextual +{{#enumUnknownDefaultCase}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +{{/enumUnknownDefaultCase}} +{{#hasEnums}} +{{/hasEnums}} +{{/kotlinx_serialization}} +{{#parcelizeModels}} +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +{{/parcelizeModels}} +{{/multiplatform}} +{{#multiplatform}} +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +{{/multiplatform}} +{{#serializableModel}} +import java.io.Serializable +{{/serializableModel}} +{{#generateRoomModels}} +import {{roomModelPackage}}.{{classname}}RoomModel +import {{packageName}}.infrastructure.ITransformForStorage +{{/generateRoomModels}} + +/** + * {{{description}}} + * +{{#allVars}} + * @param {{{name}}} {{{description}}} +{{/allVars}} + */ +{{#parcelizeModels}} +@Parcelize +{{/parcelizeModels}} +{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} +{{#isDeprecated}} +@Deprecated(message = "This schema is deprecated.") +{{/isDeprecated}} +{{>additionalModelTypeAnnotations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}data class{{/discriminator}} {{classname}}{{^discriminator}} ( + +{{#allVars}} +{{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} + +{{/allVars}} +){{/discriminator}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { +{{/vendorExtensions.x-has-data-class-body}} +{{#generateRoomModels}} + companion object { } + {{^discriminator}}override fun toRoomModel(): {{classname}}RoomModel = + {{classname}}RoomModel(roomTableId = 0, + {{#allVars}}{{#items.isPrimitiveType}}{{#isArray}}{{#isList}}{{name}} = this.{{name}},{{/isList}}{{/isArray}}{{/items.isPrimitiveType}}{{^isEnum}}{{^isArray}}{{name}} = this.{{name}},{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{name}} = this.{{name}},{{/isArray}}{{/isEnum}} + {{/allVars}} + ){{/discriminator}} +{{/generateRoomModels}} +{{#serializableModel}} + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private const val serialVersionUID: Long = 123 + } +{{/serializableModel}} +{{#discriminator}}{{#vars}}{{#required}} +{{>interface_req_var}}{{/required}}{{^required}} +{{>interface_opt_var}}{{/required}}{{/vars}}{{/discriminator}} +{{#hasEnums}} +{{#vars}} +{{#isEnum}} + /** + * {{{description}}} + * + * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} + */ + {{^multiplatform}} + {{#kotlinx_serialization}} + {{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{#enumUnknownDefaultCase}}(with = {{classname}}Serializer::class){{/enumUnknownDefaultCase}}{{/serializableModel}} + {{/kotlinx_serialization}} + {{#moshi}} + @JsonClass(generateAdapter = false) + {{/moshi}} + {{/multiplatform}} + {{#multiplatform}} + @Serializable + {{/multiplatform}} + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{{nameInPascalCase}}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{#allowableValues}} + {{#enumVars}} + {{^multiplatform}} + {{#moshi}} + @Json(name = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/moshi}} + {{#gson}} + @SerializedName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{#enumUnknownDefaultCase}}{{#-last}}@JsonEnumDefaultValue {{/-last}}{{/enumUnknownDefaultCase}}{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}} + @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/multiplatform}} + {{/enumVars}} + {{/allowableValues}} + } + {{#kotlinx_serialization}} + {{#enumUnknownDefaultCase}} + + @Serializer(forClass = {{{nameInPascalCase}}}::class) + internal object {{nameInPascalCase}}Serializer : KSerializer<{{nameInPascalCase}}> { + override val descriptor = {{{dataType}}}.serializer().descriptor + + override fun deserialize(decoder: Decoder): {{nameInPascalCase}} { + val value = decoder.decodeSerializableValue({{{dataType}}}.serializer()) + return {{nameInPascalCase}}.values().firstOrNull { it.value == value } + ?: {{nameInPascalCase}}.{{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}} + } + + override fun serialize(encoder: Encoder, value: {{nameInPascalCase}}) { + encoder.encodeSerializableValue({{{dataType}}}.serializer(), value.value) + } + } + {{/enumUnknownDefaultCase}} + {{/kotlinx_serialization}} +{{/isEnum}} +{{/vars}} +{{/hasEnums}} +{{#generateOneOfAnyOfWrappers}} + {{#gson}} + + class CustomTypeAdapterFactory : TypeAdapterFactory { + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { + return null // this class only serializes '{{classname}}' and its subtypes + } + val elementAdapter = gson.getAdapter(JsonElement::class.java) + val thisAdapter = gson.getDelegateAdapter(this, TypeToken.get({{classname}}::class.java)) + + @Suppress("UNCHECKED_CAST") + return object : TypeAdapter<{{classname}}>() { + @Throws(IOException::class) + override fun write(out: JsonWriter, value: {{classname}}) { + val obj = thisAdapter.toJsonTree(value).getAsJsonObject() + elementAdapter.write(out, obj) + } + + @Throws(IOException::class) + override fun read(jsonReader: JsonReader): {{classname}} { + val jsonElement = elementAdapter.read(jsonReader) + validateJsonElement(jsonElement) + return thisAdapter.fromJsonTree(jsonElement) + } + }.nullSafe() as TypeAdapter + } + } + + companion object { + var openapiFields = HashSet() + var openapiRequiredFields = HashSet() + + init { + {{#allVars}} + {{#-first}} + // a set of all properties/fields (JSON key names) + {{/-first}} + openapiFields.add("{{baseName}}") + {{/allVars}} + + {{#requiredVars}} + {{#-first}} + // a set of required properties/fields (JSON key names) + {{/-first}} + openapiRequiredFields.add("{{baseName}}") + {{/requiredVars}} + } + + /** + * Validates the JSON Element and throws an exception if issues found + * + * @param jsonElement JSON Element + * @throws IOException if the JSON Element is invalid with respect to {{classname}} + */ + @Throws(IOException::class) + fun validateJsonElement(jsonElement: JsonElement?) { + if (jsonElement == null) { + require(openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null + String.format("The required field(s) %s in {{{classname}}} is not found in the empty JSON string", {{classname}}.openapiRequiredFields.toString()) + } + } + {{^hasChildren}} + {{#requiredVars}} + {{#-first}} + + // check to make sure all required properties/fields are present in the JSON string + for (requiredField in openapiRequiredFields) { + requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) { + String.format("The required field `%s` is not found in the JSON string: %s", requiredField, jsonElement.toString()) + } + } + {{/-first}} + {{/requiredVars}} + {{/hasChildren}} + {{^discriminator}} + {{#hasVars}} + val jsonObj = jsonElement!!.getAsJsonObject() + {{/hasVars}} + {{#vars}} + {{#isArray}} + {{#items.isModel}} + {{#required}} + // ensure the json data is an array + if (!jsonObj.get("{{{baseName}}}").isJsonArray) { + throw IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString())) + } + + // validate the required field `{{{baseName}}}` (array) + for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) { + {{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i)) + } + {{/required}} + {{^required}} + if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { + if (jsonObj.getAsJsonArray("{{{baseName}}}") != null) { + // ensure the json data is an array + require(jsonObj["{{{baseName}}}"].isJsonArray) { + String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + + // validate the optional field `{{{baseName}}}` (array) + for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) { + {{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i)) + } + } + } + {{/required}} + {{/items.isModel}} + {{^items.isModel}} + {{^required}} + // ensure the optional json data is an array if present + if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { + require(jsonObj["{{{baseName}}}"].isJsonArray()) { + String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + } + {{/required}} + {{#required}} + // ensure the required json array is present + requireNotNull(jsonObj["{{{baseName}}}"]) { + "Expected the field `{{{baseName}}}` to be an array in the JSON string but got `null`" + } + require(jsonObj["{{{baseName}}}"].isJsonArray()) { + String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + {{/required}} + {{/items.isModel}} + {{/isArray}} + {{^isContainer}} + {{#isString}} + {{#notRequiredOrIsNullable}} + if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { + require(jsonObj.get("{{{baseName}}}").isJsonPrimitive) { + String.format("Expected the field `{{{baseName}}}` to be a primitive type in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + } + {{/notRequiredOrIsNullable}} + {{^notRequiredOrIsNullable}} + require(jsonObj["{{{baseName}}}"].isJsonPrimitive) { + String.format("Expected the field `{{{baseName}}}` to be a primitive type in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + {{/notRequiredOrIsNullable}} + {{/isString}} + {{#isModel}} + {{#required}} + // validate the required field `{{{baseName}}}` + {{{dataType}}}.validateJsonElement(jsonObj["{{{baseName}}}"]) + {{/required}} + {{^required}} + // validate the optional field `{{{baseName}}}` + if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { + {{{dataType}}}.validateJsonElement(jsonObj["{{{baseName}}}"]) + } + {{/required}} + {{/isModel}} + {{#isEnum}} + {{#required}} + // validate the required field `{{{baseName}}}` + require({{{datatypeWithEnum}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { + String.format("Expected the field `{{{baseName}}}` to be valid `{{{datatypeWithEnum}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + {{/required}} + {{^required}} + // validate the optional field `{{{baseName}}}` + if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { + require({{{datatypeWithEnum}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { + String.format("Expected the field `{{{baseName}}}` to be valid `{{{datatypeWithEnum}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + } + {{/required}} + {{/isEnum}} + {{#isEnumRef}} + {{#required}} + // validate the required field `{{{baseName}}}` + require({{{dataType}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { + String.format("Expected the field `{{{baseName}}}` to be valid `{{{dataType}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + {{/required}} + {{^required}} + // validate the optional field `{{{baseName}}}` + if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { + require({{{dataType}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { + String.format("Expected the field `{{{baseName}}}` to be valid `{{{dataType}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) + } + } + {{/required}} + {{/isEnumRef}} + {{/isContainer}} + {{/vars}} + {{/discriminator}} + } + } + {{/gson}} +{{/generateOneOfAnyOfWrappers}} + +{{#vendorExtensions.x-has-data-class-body}} +} +{{/vendorExtensions.x-has-data-class-body}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache new file mode 100644 index 000000000000..1609fa8656e4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache @@ -0,0 +1,21 @@ +{{#description}} + /* {{{.}}} */ +{{/description}} + {{^multiplatform}} + {{#moshi}} + @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") + {{/moshi}} + {{#gson}} + @SerializedName("{{{vendorExtensions.x-base-name-literal}}}") + {{/gson}} + {{#jackson}} + @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") + {{/jackson}} + {{#kotlinx_serialization}} + {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#deprecated}} + @Deprecated(message = "This property is deprecated.") + {{/deprecated}} + {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache new file mode 100644 index 000000000000..3c9387d10151 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache @@ -0,0 +1,21 @@ +{{#description}} + /* {{{.}}} */ +{{/description}} + {{^multiplatform}} + {{#moshi}} + @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") + {{/moshi}} + {{#gson}} + @SerializedName("{{{vendorExtensions.x-base-name-literal}}}") + {{/gson}} + {{#jackson}} + @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") + {{/jackson}} + {{#kotlinx_serialization}} + {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#deprecated}} + @Deprecated(message = "This property is deprecated.") + {{/deprecated}} + {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache new file mode 100644 index 000000000000..acbd45f92c7e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache @@ -0,0 +1,113 @@ +{{^multiplatform}} +{{#gson}} +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +{{/moshi}} +{{#jackson}} +{{#enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +{{/enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonProperty +{{/jackson}} +{{#kotlinx_serialization}} +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +{{#enumUnknownDefaultCase}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +{{/enumUnknownDefaultCase}} +{{/kotlinx_serialization}} +{{/multiplatform}} +{{#multiplatform}} +import kotlinx.serialization.* +{{/multiplatform}} + +/** + * {{{description}}} + * + * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} + */ +{{#multiplatform}}@Serializable{{/multiplatform}}{{#kotlinx_serialization}}@Serializable{{#enumUnknownDefaultCase}}(with = {{classname}}Serializer::class){{/enumUnknownDefaultCase}}{{/kotlinx_serialization}} +{{^multiplatform}} +{{#moshi}} +@JsonClass(generateAdapter = false) +{{/moshi}} +{{/multiplatform}} +{{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{classname}}(val value: {{{dataType}}}) { +{{#allowableValues}}{{#enumVars}} + {{^multiplatform}} + {{#moshi}} + @Json(name = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) + {{/moshi}} + {{#gson}} + @SerializedName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}){{#enumUnknownDefaultCase}}{{#-last}} @JsonEnumDefaultValue{{/-last}}{{/enumUnknownDefaultCase}} + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}} + @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) + {{/multiplatform}} + {{#isArray}} + {{#isList}} + {{&name}}(listOf({{{value}}})){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/isList}} + {{^isList}} + {{&name}}(arrayOf({{{value}}})){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/isList}} + {{/isArray}} + {{^isArray}} + {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/isArray}} +{{/enumVars}}{{/allowableValues}} + /** + * Override [toString()] to avoid using the enum variable name as the value, and instead use + * the actual value defined in the API spec file. + * + * This solves a problem when the variable name and its value are different, and ensures that + * the client sends the correct enum values to the server always. + */ + override fun toString(): kotlin.String = value{{^isString}}.toString(){{/isString}} + + companion object { + /** + * Converts the provided [data] to a [String] on success, null otherwise. + */ + fun encode(data: kotlin.Any?): kotlin.String? = if (data is {{classname}}) "$data" else null + + /** + * Returns a valid [{{classname}}] for [data], null otherwise. + */ + fun decode(data: kotlin.Any?): {{classname}}? = data?.let { + val normalizedData = "$it".lowercase() + values().firstOrNull { value -> + it == value || normalizedData == "$value".lowercase() + } + } + } +}{{#kotlinx_serialization}}{{#enumUnknownDefaultCase}} + +@Serializer(forClass = {{classname}}::class) +internal object {{classname}}Serializer : KSerializer<{{classname}}> { + override val descriptor = {{{dataType}}}.serializer().descriptor + + override fun deserialize(decoder: Decoder): {{classname}} { + val value = decoder.decodeSerializableValue({{{dataType}}}.serializer()) + return {{classname}}.values().firstOrNull { it.value == value } + ?: {{classname}}.{{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}} + } + + override fun serialize(encoder: Encoder, value: {{classname}}) { + encoder.encodeSerializableValue({{{dataType}}}.serializer(), value.value) + } +}{{/enumUnknownDefaultCase}}{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache new file mode 100644 index 000000000000..fcb3d7e61aa6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache @@ -0,0 +1,7 @@ +# {{classname}} + +## Enum + +{{#allowableValues}}{{#enumVars}} + * `{{name}}` (value: `{{{value}}}`) +{{/enumVars}}{{/allowableValues}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.jar b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache new file mode 100644 index 000000000000..6eb2a6bb7b23 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache @@ -0,0 +1,12 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +{{#jvm-volley}} +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-all.zip +{{/jvm-volley}} +{{^jvm-volley}} +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +{{/jvm-volley}} +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache new file mode 100644 index 000000000000..107acd32c4e6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache new file mode 100755 index 000000000000..9d0ce634cb11 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while +APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path +[ -h "$app_path" ] +do +ls=$( ls -ld "$app_path" ) +link=${ls#*' -> '} +case $link in #( +/*) app_path=$link ;; #( +*) app_path=$APP_HOME$link ;; +esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { +echo "$*" +} >&2 + +die () { +echo +echo "$*" +echo +exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( +CYGWIN* ) cygwin=true ;; #( +Darwin* ) darwin=true ;; #( +MSYS* | MINGW* ) msys=true ;; #( +NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then +if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +# IBM's JDK on AIX uses strange locations for the executables +JAVACMD=$JAVA_HOME/jre/sh/java +else +JAVACMD=$JAVA_HOME/bin/java +fi +if [ ! -x "$JAVACMD" ] ; then +die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi +else +JAVACMD=java +if ! command -v java >/dev/null 2>&1 +then +die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then +case $MAX_FD in #( +max*) +# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. +# shellcheck disable=SC2039,SC3045 +MAX_FD=$( ulimit -H -n ) || +warn "Could not query maximum file descriptor limit" +esac +case $MAX_FD in #( +'' | soft) :;; #( +*) +# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. +# shellcheck disable=SC2039,SC3045 +ulimit -n "$MAX_FD" || +warn "Could not set maximum file descriptor limit to $MAX_FD" +esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then +APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) +CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + +JAVACMD=$( cygpath --unix "$JAVACMD" ) + +# Now convert the arguments - kludge to limit ourselves to /bin/sh +for arg do +if +case $arg in #( +-*) false ;; # don't mess with options #( +/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath +[ -e "$t" ] ;; #( +*) false ;; +esac +then +arg=$( cygpath --path --ignore --mixed "$arg" ) +fi +# Roll the args list around exactly as many times as the number of +# args, so each arg winds up back in the position where it started, but +# possibly modified. +# +# NB: a `for` loop captures its iteration list before it begins, so +# changing the positional parameters here affects neither the number of +# iterations, nor the values presented in `arg`. +shift # remove old arg +set -- "$@" "$arg" # push replacement arg +done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ +"-Dorg.gradle.appname=$APP_BASE_NAME" \ +-classpath "$CLASSPATH" \ +org.gradle.wrapper.GradleWrapperMain \ +"$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then +die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( +printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | +xargs -n1 | +sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | +tr '\n' ' ' +)" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache new file mode 100644 index 000000000000..523503eef9d0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache @@ -0,0 +1,23 @@ +package {{packageName}}.infrastructure + +{{#nonPublicApi}}internal {{/nonPublicApi}}typealias MultiValueMap = MutableMap> + +{{#nonPublicApi}}internal {{/nonPublicApi}}fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipe" -> "|" + "space" -> " " + else -> "" +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } + +{{#nonPublicApi}}internal {{/nonPublicApi}}fun toMultiValue(items: Array, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter) + = toMultiValue(items.asIterable(), collectionFormat, map) + +{{#nonPublicApi}}internal {{/nonPublicApi}}fun toMultiValue(items: Iterable, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List { + return when(collectionFormat) { + "multi" -> items.map(map) + else -> listOf(items.joinToString(separator = collectionDelimiter(collectionFormat), transform = map)) + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache new file mode 100644 index 000000000000..f70c18eff465 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache @@ -0,0 +1,11 @@ +package {{packageName}}.infrastructure + +/** + * Defines a config object for a given part of a multi-part request. + * NOTE: Headers is a Map because rfc2616 defines + * multi-valued headers as csv-only. + */ +{{#nonPublicApi}}internal {{/nonPublicApi}}data class PartConfig( + val headers: MutableMap = mutableMapOf(), + val body: T? = null +) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache new file mode 100644 index 000000000000..7de4c68ae02e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache @@ -0,0 +1,19 @@ +package {{packageName}}.infrastructure + +/** + * Defines a config object for a given request. + * 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. + */ +{{#nonPublicApi}}internal {{/nonPublicApi}}data class RequestConfig( + val method: RequestMethod, + val path: String, + val headers: MutableMap = mutableMapOf(), + val params: MutableMap = mutableMapOf(), + val query: MutableMap> = mutableMapOf(), + val requiresAuthentication: Boolean, + val body: T? = null +) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache new file mode 100644 index 000000000000..cb59c4f58c8c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache @@ -0,0 +1,8 @@ +package {{packageName}}.infrastructure + +/** + * Provides enumerated HTTP verbs + */ +{{#nonPublicApi}}internal {{/nonPublicApi}}enum class RequestMethod { + GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache new file mode 100644 index 000000000000..260be5c3caa8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache @@ -0,0 +1,18 @@ +{{#description}} + /* {{{.}}} */ +{{/description}} + {{^multiplatform}} + {{#moshi}} + @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") + {{/moshi}} + {{#gson}} + @get:SerializedName("{{{vendorExtensions.x-base-name-literal}}}") + {{/gson}} + {{#jackson}} + @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") + {{/jackson}} + {{#kotlinx_serialization}} + {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache new file mode 100644 index 000000000000..6560b23d9626 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache @@ -0,0 +1,18 @@ +{{#description}} + /* {{{.}}} */ +{{/description}} + {{^multiplatform}} + {{#moshi}} + @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") + {{/moshi}} + {{#gson}} + @get:SerializedName("{{{vendorExtensions.x-base-name-literal}}}") + {{/gson}} + {{#jackson}} + @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") + {{/jackson}} + {{#kotlinx_serialization}} + {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache new file mode 100644 index 000000000000..098ff54c6a1c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache @@ -0,0 +1,21 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +import java.util.concurrent.atomic.AtomicBoolean + +@Serializer(forClass = AtomicBoolean::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object AtomicBooleanAdapter : KSerializer { + override fun serialize(encoder: Encoder, value: AtomicBoolean) { + encoder.encodeBoolean(value.get()) + } + + override fun deserialize(decoder: Decoder): AtomicBoolean = AtomicBoolean(decoder.decodeBoolean()) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicBoolean", PrimitiveKind.BOOLEAN) +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache new file mode 100644 index 000000000000..6e16b5b78a6d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache @@ -0,0 +1,21 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +import java.util.concurrent.atomic.AtomicInteger + +@Serializer(forClass = AtomicInteger::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object AtomicIntegerAdapter : KSerializer { + override fun serialize(encoder: Encoder, value: AtomicInteger) { + encoder.encodeInt(value.get()) + } + + override fun deserialize(decoder: Decoder): AtomicInteger = AtomicInteger(decoder.decodeInt()) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicInteger", PrimitiveKind.INT) +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache new file mode 100644 index 000000000000..bc2142c676a7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache @@ -0,0 +1,21 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +import java.util.concurrent.atomic.AtomicLong + +@Serializer(forClass = AtomicLong::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object AtomicLongAdapter : KSerializer { + override fun serialize(encoder: Encoder, value: AtomicLong) { + encoder.encodeLong(value.get()) + } + + override fun deserialize(decoder: Decoder): AtomicLong = AtomicLong(decoder.decodeLong()) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicLong", PrimitiveKind.LONG) +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache new file mode 100644 index 000000000000..fd08ed4a04cc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache @@ -0,0 +1,38 @@ +package {{packageName}}.infrastructure + +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +import java.math.BigDecimal + +{{#kotlinx_serialization}} +@Serializer(forClass = BigDecimal::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object BigDecimalAdapter : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING) + override fun deserialize(decoder: Decoder): BigDecimal = BigDecimal(decoder.decodeString()) + override fun serialize(encoder: Encoder, value: BigDecimal) = encoder.encodeString(value.toPlainString()) +} +{{/kotlinx_serialization}} +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class BigDecimalAdapter { + @ToJson + fun toJson(value: BigDecimal): String { + return value.toPlainString() + } + + @FromJson + fun fromJson(value: String): BigDecimal { + return BigDecimal(value) + } +} +{{/moshi}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache new file mode 100644 index 000000000000..e7403a6882af --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache @@ -0,0 +1,43 @@ +package {{packageName}}.infrastructure + +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +import java.math.BigInteger + +{{#kotlinx_serialization}} +@Serializer(forClass = BigInteger::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object BigIntegerAdapter : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigInteger", PrimitiveKind.STRING) + override fun deserialize(decoder: Decoder): BigInteger { + return BigInteger(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: BigInteger) { + encoder.encodeString(value.toString()) + } +} +{{/kotlinx_serialization}} +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class BigIntegerAdapter { + @ToJson + fun toJson(value: BigInteger): String { + return value.toString() + } + + @FromJson + fun fromJson(value: String): BigInteger { + return BigInteger(value) + } +} +{{/moshi}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache new file mode 100644 index 000000000000..edc967afdbc2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache @@ -0,0 +1,50 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +{{#gson}} +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.stream.JsonToken.NULL +import java.io.IOException +{{/gson}} + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class ByteArrayAdapter { + @ToJson + fun toJson(data: ByteArray): String = String(data) + + @FromJson + fun fromJson(data: String): ByteArray = data.toByteArray() +} +{{/moshi}} +{{#gson}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class ByteArrayAdapter : TypeAdapter() { + @Throws(IOException::class) + override fun write(out: JsonWriter?, value: ByteArray?) { + if (value == null) { + out?.nullValue() + } else { + out?.value(String(value)) + } + } + + @Throws(IOException::class) + override fun read(out: JsonReader?): ByteArray? { + out ?: return null + + when (out.peek()) { + NULL -> { + out.nextNull() + return null + } + else -> { + return out.nextString().toByteArray() + } + } + } +} +{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache new file mode 100644 index 000000000000..66d389c3f6d0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache @@ -0,0 +1,56 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +{{#gson}} +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.stream.JsonToken.NULL +import java.io.IOException +{{/gson}} +import kotlinx.datetime.Instant + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class InstantAdapter { + @ToJson + fun toJson(value: Instant): String { + return value.toString() + } + + @FromJson + fun fromJson(value: String): Instant { + return Instant.parse(value) + } + +} +{{/moshi}} +{{#gson}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class InstantAdapter : TypeAdapter() { + @Throws(IOException::class) + override fun write(out: JsonWriter?, value: Instant?) { + if (value == null) { + out?.nullValue() + } else { + out?.value(value.toString()) + } + } + + @Throws(IOException::class) + override fun read(out: JsonReader?): Instant? { + out ?: return null + + when (out.peek()) { + NULL -> { + out.nextNull() + return null + } + else -> { + return Instant.parse(out.nextString()) + } + } + } +} +{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache new file mode 100644 index 000000000000..6809c74c3a77 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache @@ -0,0 +1,103 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +{{#gson}} +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.stream.JsonToken.NULL +import java.io.IOException +{{/gson}} +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{^threetenbp}} +{{^kotlinx-datetime}} +import java.time.LocalDate +import java.time.format.DateTimeFormatter +{{/kotlinx-datetime}} +{{/threetenbp}} +{{#threetenbp}} +import org.threeten.bp.LocalDate +import org.threeten.bp.format.DateTimeFormatter +{{/threetenbp}} +{{#kotlinx-datetime}} +import kotlinx.datetime.LocalDate +{{/kotlinx-datetime}} + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateAdapter { + @ToJson + fun toJson(value: LocalDate): String { + {{#kotlinx-datetime}} + return value.toString() + {{/kotlinx-datetime}} + {{^kotlinx-datetime}} + return DateTimeFormatter.ISO_LOCAL_DATE.format(value) + {{/kotlinx-datetime}} + } + + @FromJson + fun fromJson(value: String): LocalDate { + return LocalDate.parse(value{{^kotlinx-datetime}}, DateTimeFormatter.ISO_LOCAL_DATE{{/kotlinx-datetime}}) + } + +} +{{/moshi}} +{{#gson}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateAdapter({{^kotlinx-datetime}}private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE{{/kotlinx-datetime}}) : TypeAdapter() { + @Throws(IOException::class) + override fun write(out: JsonWriter?, value: LocalDate?) { + if (value == null) { + out?.nullValue() + } else { + {{#kotlinx-datetime}} + out?.value(value.toString()) + {{/kotlinx-datetime}} + {{^kotlinx-datetime}} + out?.value(formatter.format(value)) + {{/kotlinx-datetime}} + } + } + + @Throws(IOException::class) + override fun read(out: JsonReader?): LocalDate? { + out ?: return null + + when (out.peek()) { + NULL -> { + out.nextNull() + return null + } + else -> { + return LocalDate.parse(out.nextString(){{^kotlinx-datetime}}, formatter{{/kotlinx-datetime}}) + } + } + } +} +{{/gson}} +{{#kotlinx_serialization}} +{{^kotlinx-datetime}} +@Serializer(forClass = LocalDate::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object LocalDateAdapter : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: LocalDate) { + encoder.encodeString(DateTimeFormatter.ISO_LOCAL_DATE.format(value)) + } + + override fun deserialize(decoder: Decoder): LocalDate { + return LocalDate.parse(decoder.decodeString(), DateTimeFormatter.ISO_LOCAL_DATE) + } +} +{{/kotlinx-datetime}} +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache new file mode 100644 index 000000000000..f56cf12d5b77 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache @@ -0,0 +1,103 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +{{#gson}} +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.stream.JsonToken.NULL +import java.io.IOException +{{/gson}} +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{^threetenbp}} +{{^kotlinx-datetime}} +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +{{/kotlinx-datetime}} +{{/threetenbp}} +{{#threetenbp}} +import org.threeten.bp.LocalDateTime +import org.threeten.bp.format.DateTimeFormatter +{{/threetenbp}} +{{#kotlinx-datetime}} +import kotlinx.datetime.LocalDateTime +{{/kotlinx-datetime}} + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateTimeAdapter { + @ToJson + fun toJson(value: LocalDateTime): String { + {{#kotlinx-datetime}} + return value.toString() + {{/kotlinx-datetime}} + {{^kotlinx-datetime}} + return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value) + {{/kotlinx-datetime}} + } + + @FromJson + fun fromJson(value: String): LocalDateTime { + return LocalDateTime.parse(value{{^kotlinx-datetime}}, DateTimeFormatter.ISO_LOCAL_DATE_TIME{{/kotlinx-datetime}}) + } + +} +{{/moshi}} +{{#gson}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateTimeAdapter({{^kotlinx-datetime}}private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME{{/kotlinx-datetime}}) : TypeAdapter() { + @Throws(IOException::class) + override fun write(out: JsonWriter?, value: LocalDateTime?) { + if (value == null) { + out?.nullValue() + } else { + {{#kotlinx-datetime}} + out?.value(value.toString()) + {{/kotlinx-datetime}} + {{^kotlinx-datetime}} + out?.value(formatter.format(value)) + {{/kotlinx-datetime}} + } + } + + @Throws(IOException::class) + override fun read(out: JsonReader?): LocalDateTime? { + out ?: return null + + when (out.peek()) { + NULL -> { + out.nextNull() + return null + } + else -> { + return LocalDateTime.parse(out.nextString(){{^kotlinx-datetime}}, formatter{{/kotlinx-datetime}}) + } + } + } +} +{{/gson}} +{{#kotlinx_serialization}} +{{^kotlinx-datetime}} +@Serializer(forClass = LocalDateTime::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object LocalDateTimeAdapter : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: LocalDateTime) { + encoder.encodeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value)) + } + + override fun deserialize(decoder: Decoder): LocalDateTime { + return LocalDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME) + } +} +{{/kotlinx-datetime}} +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache new file mode 100644 index 000000000000..3a294f8ef7df --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache @@ -0,0 +1,56 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +{{#gson}} +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.stream.JsonToken.NULL +import java.io.IOException +{{/gson}} +import kotlinx.datetime.LocalTime + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalTimeAdapter { + @ToJson + fun toJson(value: LocalTime): String { + return value.toString() + } + + @FromJson + fun fromJson(value: String): LocalTime { + return LocalTime.parse(value) + } + +} +{{/moshi}} +{{#gson}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalTimeAdapter : TypeAdapter() { + @Throws(IOException::class) + override fun write(out: JsonWriter?, value: LocalTime?) { + if (value == null) { + out?.nullValue() + } else { + out?.value(value.toString()) + } + } + + @Throws(IOException::class) + override fun read(out: JsonReader?): LocalTime? { + out ?: return null + + when (out.peek()) { + NULL -> { + out.nextNull() + return null + } + else -> { + return LocalTime.parse(out.nextString()) + } + } + } +} +{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache new file mode 100644 index 000000000000..b2d47121654b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache @@ -0,0 +1,86 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +{{#gson}} +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.stream.JsonToken.NULL +import java.io.IOException +{{/gson}} +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{^threetenbp}} +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +{{/threetenbp}} +{{#threetenbp}} +import org.threeten.bp.OffsetDateTime +import org.threeten.bp.format.DateTimeFormatter +{{/threetenbp}} + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class OffsetDateTimeAdapter { + @ToJson + fun toJson(value: OffsetDateTime): String { + return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value) + } + + @FromJson + fun fromJson(value: String): OffsetDateTime { + return OffsetDateTime.parse(value, DateTimeFormatter.ISO_OFFSET_DATE_TIME) + } + +} +{{/moshi}} +{{#gson}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class OffsetDateTimeAdapter(private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME) : TypeAdapter() { + @Throws(IOException::class) + override fun write(out: JsonWriter?, value: OffsetDateTime?) { + if (value == null) { + out?.nullValue() + } else { + out?.value(formatter.format(value)) + } + } + + @Throws(IOException::class) + override fun read(out: JsonReader?): OffsetDateTime? { + out ?: return null + + when (out.peek()) { + NULL -> { + out.nextNull() + return null + } + else -> { + return OffsetDateTime.parse(out.nextString(), formatter) + } + } + } +} +{{/gson}} +{{#kotlinx_serialization}} +@Serializer(forClass = OffsetDateTime::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object OffsetDateTimeAdapter : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("OffsetDateTime", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: OffsetDateTime) { + encoder.encodeString(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value)) + } + + override fun deserialize(decoder: Decoder): OffsetDateTime { + return OffsetDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME) + } +} +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache new file mode 100644 index 000000000000..6b78b03b7af8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache @@ -0,0 +1,156 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.Moshi +{{#enumUnknownDefaultCase}} +import com.squareup.moshi.adapters.EnumJsonAdapter +{{/enumUnknownDefaultCase}} +{{^moshiCodeGen}} +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +{{/moshiCodeGen}} +{{/moshi}} +{{#gson}} +import com.google.gson.Gson +import com.google.gson.GsonBuilder +{{^threetenbp}} +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.OffsetDateTime +{{/threetenbp}} +{{#threetenbp}} +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.OffsetDateTime +{{/threetenbp}} +{{#kotlinx-datetime}} +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalTime +{{/kotlinx-datetime}} +import java.util.UUID +{{/gson}} +{{#jackson}} +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +{{/jackson}} +{{#kotlinx_serialization}} +import java.math.BigDecimal +import java.math.BigInteger +{{^threetenbp}} +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.OffsetDateTime +{{/threetenbp}} +{{#threetenbp}} +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.OffsetDateTime +{{/threetenbp}} +import java.util.UUID +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import java.net.URI +import java.net.URL +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong +{{/kotlinx_serialization}} + +{{#nonPublicApi}}internal {{/nonPublicApi}}object Serializer { +{{#moshi}} + @JvmStatic + val moshiBuilder: Moshi.Builder = Moshi.Builder() + .add(OffsetDateTimeAdapter()) + {{#kotlinx-datetime}} + .add(InstantAdapter()) + .add(LocalTimeAdapter()) + {{/kotlinx-datetime}} + .add(LocalDateTimeAdapter()) + .add(LocalDateAdapter()) + .add(UUIDAdapter()) + .add(ByteArrayAdapter()) + .add(URIAdapter()) + {{^moshiCodeGen}} + .add(KotlinJsonAdapterFactory()) + {{/moshiCodeGen}} + .add(BigDecimalAdapter()) + .add(BigIntegerAdapter()) + + @JvmStatic + val moshi: Moshi by lazy { +{{#enumUnknownDefaultCase}} + SerializerHelper.addEnumUnknownDefaultCase(moshiBuilder) +{{/enumUnknownDefaultCase}} + moshiBuilder.build() + } +{{/moshi}} +{{#gson}} + @JvmStatic + val gsonBuilder: GsonBuilder = GsonBuilder() + .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) + {{#kotlinx-datetime}} + .registerTypeAdapter(Instant::class.java, InstantAdapter()) + .registerTypeAdapter(LocalTime::class.java, LocalTimeAdapter()) + {{/kotlinx-datetime}} + .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter()) + .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) + .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) + + @JvmStatic + val gson: Gson by lazy { + gsonBuilder.create() + } +{{/gson}} +{{#jackson}} + @JvmStatic + val jacksonObjectMapper: ObjectMapper = jacksonObjectMapper() + .findAndRegisterModules() + .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) + {{#enumUnknownDefaultCase}} + .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true) + {{/enumUnknownDefaultCase}} + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}}) +{{/jackson}} +{{#kotlinx_serialization}} + @Deprecated("Use Serializer.kotlinxSerializationAdapters instead", replaceWith = ReplaceWith("Serializer.kotlinxSerializationAdapters")) + @JvmStatic + val kotlinSerializationAdapters: SerializersModule + get() { return kotlinxSerializationAdapters } + + @JvmStatic + val kotlinxSerializationAdapters = SerializersModule { + contextual(BigDecimal::class, BigDecimalAdapter) + contextual(BigInteger::class, BigIntegerAdapter) + {{^kotlinx-datetime}} + contextual(LocalDate::class, LocalDateAdapter) + contextual(LocalDateTime::class, LocalDateTimeAdapter) + contextual(OffsetDateTime::class, OffsetDateTimeAdapter) + {{/kotlinx-datetime}} + contextual(UUID::class, UUIDAdapter) + contextual(AtomicInteger::class, AtomicIntegerAdapter) + contextual(AtomicLong::class, AtomicLongAdapter) + contextual(AtomicBoolean::class, AtomicBooleanAdapter) + contextual(URI::class, URIAdapter) + contextual(URL::class, URLAdapter) + contextual(StringBuilder::class, StringBuilderAdapter) + } + + @Deprecated("Use Serializer.kotlinxSerializationJson instead", replaceWith = ReplaceWith("Serializer.kotlinxSerializationJson")) + @JvmStatic + val jvmJson: Json + get() { return kotlinxSerializationJson } + + @JvmStatic + val kotlinxSerializationJson: Json by lazy { + Json { + serializersModule = kotlinxSerializationAdapters + encodeDefaults = true + ignoreUnknownKeys = true + isLenient = true + } + } +{{/kotlinx_serialization}} +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache new file mode 100644 index 000000000000..27f4d1ee617c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache @@ -0,0 +1,50 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.Moshi +{{#enumUnknownDefaultCase}} +import com.squareup.moshi.adapters.EnumJsonAdapter +{{/enumUnknownDefaultCase}} +{{/moshi}} + +{{#nonPublicApi}}internal {{/nonPublicApi}}object SerializerHelper { +{{#moshi}} + fun addEnumUnknownDefaultCase(moshiBuilder: Moshi.Builder): Moshi.Builder { + return moshiBuilder +{{#enumUnknownDefaultCase}} +{{#models}} +{{#model}} +{{#isEnum}} +{{#allowableValues}} +{{#enumVars}} +{{#-last}} + .add({{modelPackage}}.{{classname}}::class.java, EnumJsonAdapter.create({{modelPackage}}.{{classname}}::class.java) + .withUnknownFallback({{modelPackage}}.{{classname}}.{{&name}})) +{{/-last}} +{{/enumVars}} +{{/allowableValues}} +{{/isEnum}} +{{^isEnum}} +{{^isAlias}} +{{#hasEnums}} +{{#vars}} +{{#isEnum}} +{{#allowableValues}} +{{#enumVars}} +{{#-last}} + .add({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}::class.java, EnumJsonAdapter.create({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}::class.java) + .withUnknownFallback({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}.{{&name}})) +{{/-last}} +{{/enumVars}} +{{/allowableValues}} +{{/isEnum}} +{{/vars}} +{{/hasEnums}} +{{/isAlias}} +{{/isEnum}} +{{/model}} +{{/models}} +{{/enumUnknownDefaultCase}} + } +{{/moshi}} +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache new file mode 100644 index 000000000000..3d5367030716 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache @@ -0,0 +1,20 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor + +@Serializer(forClass = StringBuilder::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object StringBuilderAdapter : KSerializer { + override fun serialize(encoder: Encoder, value: StringBuilder) { + encoder.encodeString(value.toString()) + } + + override fun deserialize(decoder: Decoder): StringBuilder = StringBuilder(decoder.decodeString()) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StringBuilder", PrimitiveKind.STRING) +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache new file mode 100644 index 000000000000..563b39cdb9a9 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache @@ -0,0 +1,38 @@ +package {{packageName}}.infrastructure + +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +import java.net.URI + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class URIAdapter { + @ToJson + fun toJson(uri: URI) = uri.toString() + + @FromJson + fun fromJson(s: String): URI = URI.create(s) +} +{{/moshi}} +{{#kotlinx_serialization}} +@Serializer(forClass = URI::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object URIAdapter : KSerializer { + override fun serialize(encoder: Encoder, value: URI) { + encoder.encodeString(value.toASCIIString()) + } + + override fun deserialize(decoder: Decoder): URI = URI(decoder.decodeString()) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URI", PrimitiveKind.STRING) +} +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache new file mode 100644 index 000000000000..b437ff89ae0e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache @@ -0,0 +1,21 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +import java.net.URL + +@Serializer(forClass = URL::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object URLAdapter : KSerializer { + override fun serialize(encoder: Encoder, value: URL) { + encoder.encodeString(value.toExternalForm()) + } + + override fun deserialize(decoder: Decoder): URL = URL(decoder.decodeString()) + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URL", PrimitiveKind.STRING) +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache new file mode 100644 index 000000000000..925532d6aa3f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache @@ -0,0 +1,40 @@ +package {{packageName}}.infrastructure + +{{#kotlinx_serialization}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.SerialDescriptor +{{/kotlinx_serialization}} +{{#moshi}} +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +{{/moshi}} +import java.util.UUID + +{{#moshi}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class UUIDAdapter { + @ToJson + fun toJson(uuid: UUID) = uuid.toString() + + @FromJson + fun fromJson(s: String): UUID = UUID.fromString(s) +} +{{/moshi}} +{{#kotlinx_serialization}} +@Serializer(forClass = UUID::class) +{{#nonPublicApi}}internal {{/nonPublicApi}}object UUIDAdapter : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: UUID) { + encoder.encodeString(value.toString()) + } + + override fun deserialize(decoder: Decoder): UUID { + return UUID.fromString(decoder.decodeString()) + } +} +{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache new file mode 100644 index 000000000000..6da4eba50603 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache @@ -0,0 +1,11 @@ +-keepattributes *Annotation*, InnerClasses +-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations + +# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer +-keepclassmembers class kotlinx.serialization.json.** { *** Companion; } +-keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); } + +# project specific. +-keep,includedescriptorclasses class {{modelPackage}}.**$$serializer { *; } +-keepclassmembers class {{modelPackage}}.** { *** Companion; } +-keepclasseswithmembers class {{modelPackage}}.** { kotlinx.serialization.KSerializer serializer(...); } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache new file mode 100644 index 000000000000..c6ca0ce0c06d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache @@ -0,0 +1,152 @@ +{{>licenseInfo}} +package {{apiPackage}} + +{{#imports}}import {{import}} +{{/imports}} + +import {{packageName}}.infrastructure.* +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import io.ktor.http.ParametersBuilder +{{#gson}} +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import java.text.DateFormat +{{/gson}} +{{#jackson}} +import com.fasterxml.jackson.databind.ObjectMapper +{{/jackson}} + +{{#operations}} + {{#nonPublicApi}}internal {{/nonPublicApi}}open class {{classname}}( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + {{#gson}} + jsonBlock: GsonBuilder.() -> Unit = ApiClient.JSON_DEFAULT, + {{/gson}} + {{#jackson}} + jsonBlock: ObjectMapper.() -> Unit = ApiClient.JSON_DEFAULT, + {{/jackson}} + ) : ApiClient( + baseUrl, + httpClientEngine, + httpClientConfig, + {{#gson}} + jsonBlock, + {{/gson}} + {{#jackson}} + jsonBlock, + {{/jackson}} + ) { + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}} * @param {{{paramName}}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}} * @return {{{returnType}}}{{^returnType}}void{{/returnType}} + */ + {{#returnType}} + @Suppress("UNCHECKED_CAST") + {{/returnType}} + open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}> { + + val localVariableAuthNames = listOf({{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}}) + + val localVariableBody = {{#hasBodyParam}}{{#bodyParam}}{{{paramName}}}{{/bodyParam}}{{/hasBodyParam}} + {{^hasBodyParam}} + {{#hasFormParams}} + {{#isMultipart}} + formData { + {{#formParams}} + {{#isFile}} + {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } + {{/isFile}} + {{^isFile}} + {{^isArray}} + {{^isString}} + {{^isNumber}} + {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}.toString()) } + {{/isNumber}} + {{#isNumber}} + {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } + {{/isNumber}} + {{/isString}} + {{#isString}} + {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } + {{/isString}} + {{/isArray}} + {{#isArray}} + for (x in {{paramName}} ?: listOf()) { + append("{{{baseName}}}", x.toString()) + } + {{/isArray}} + {{/isFile}} + {{/formParams}} + } + {{/isMultipart}} + {{^isMultipart}} + ParametersBuilder().also { + {{#formParams}} + {{#isFile}} + {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}) } + {{/isFile}} + {{^isFile}} + {{^isArray}} + {{^isString}} + {{^isNumber}} + {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}.toString()) } + {{/isNumber}} + {{#isNumber}} + {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}) } + {{/isNumber}} + {{/isString}} + {{#isString}} + {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}) } + {{/isString}} + {{/isArray}} + {{#isArray}} + for (x in {{paramName}} ?: listOf()) { + append("{{{baseName}}}", x.toString()) + } + {{/isArray}} + {{/isFile}} + {{/formParams}} + }.build() + {{/isMultipart}} + {{/hasFormParams}} + {{^hasFormParams}} + io.ktor.client.utils.EmptyContent + {{/hasFormParams}} + {{/hasBodyParam}} + + val localVariableQuery = mutableMapOf>() + {{#queryParams}} + {{{paramName}}}?.apply { localVariableQuery["{{baseName}}"] = {{#isContainer}}toMultiValue(this, "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf("${{{paramName}}}"){{/isContainer}} } + {{/queryParams}} + + val localVariableHeaders = mutableMapOf() + {{#headerParams}} + {{{paramName}}}?.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } + {{/headerParams}} + + val localVariableConfig = RequestConfig( + RequestMethod.{{httpMethod}}, + "{{path}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", "${{{paramName}}}"){{/pathParams}}, + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, + ) + + return {{#hasBodyParam}}jsonRequest{{/hasBodyParam}}{{^hasBodyParam}}{{#hasFormParams}}{{#isMultipart}}multipartFormRequest{{/isMultipart}}{{^isMultipart}}urlEncodedFormRequest{{/isMultipart}}{{/hasFormParams}}{{^hasFormParams}}request{{/hasFormParams}}{{/hasBodyParam}}( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).wrap() + } + + {{/operation}} + } +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache new file mode 100644 index 000000000000..618fd7a88903 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache @@ -0,0 +1,16 @@ +package {{packageName}}.auth + +class ApiKeyAuth(private val location: String, val paramName: String) : Authentication { + var apiKey: String? = null + var apiKeyPrefix: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val key: String = apiKey ?: return + val prefix: String? = apiKeyPrefix + val value: String = if (prefix != null) "$prefix $key" else key + when (location) { + "query" -> query[paramName] = listOf(value) + "header" -> headers[paramName] = value + } + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache new file mode 100644 index 000000000000..1aab9156d98b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache @@ -0,0 +1,13 @@ +package {{packageName}}.auth + +interface Authentication { + + /** + * Apply authentication settings to header and query params. + * + * @param query Query parameters. + * @param headers Header parameters. + */ + fun apply(query: MutableMap>, headers: MutableMap) + +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache new file mode 100644 index 000000000000..26325424e5dd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache @@ -0,0 +1,17 @@ +package {{packageName}}.auth + +import io.ktor.util.InternalAPI +import io.ktor.util.encodeBase64 + +class HttpBasicAuth : Authentication { + var username: String? = null + var password: String? = null + + @OptIn(InternalAPI::class) + override fun apply(query: MutableMap>, headers: MutableMap) { + if (username == null && password == null) return + val str = (username ?: "") + ":" + (password ?: "") + val auth = str.encodeBase64() + headers["Authorization"] = "Basic $auth" + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache new file mode 100644 index 000000000000..982389d09609 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache @@ -0,0 +1,14 @@ +package {{packageName}}.auth + +class HttpBearerAuth(private val scheme: String?) : Authentication { + var bearerToken: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val token: String = bearerToken ?: return + headers["Authorization"] = (if (scheme != null) upperCaseBearer(scheme)!! + " " else "") + token + } + + private fun upperCaseBearer(scheme: String): String? { + return if ("bearer".equals(scheme, ignoreCase = true)) "Bearer" else scheme + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache new file mode 100644 index 000000000000..98bb449a6096 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache @@ -0,0 +1,10 @@ +package {{packageName}}.auth + +class OAuth : Authentication { + var accessToken: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val token: String = accessToken ?: return + headers["Authorization"] = "Bearer $token" + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..c189f9f9ed93 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,229 @@ +package {{packageName}}.infrastructure + + +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.forms.FormDataContent +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.header +import io.ktor.client.request.parameter +import io.ktor.client.request.request +import io.ktor.client.request.setBody +import io.ktor.client.statement.HttpResponse +import io.ktor.http.contentType +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.Parameters +import io.ktor.http.URLBuilder +import io.ktor.http.content.PartData +import io.ktor.http.encodeURLQueryComponent +import io.ktor.http.encodedPath +import io.ktor.http.takeFrom +{{#gson}} +import io.ktor.serialization.gson.* +import com.google.gson.GsonBuilder +import java.text.DateFormat +{{/gson}} +{{#jackson}} +import io.ktor.serialization.jackson.* +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.core.util.DefaultIndenter +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter +{{/jackson}} +import {{packageName}}.auth.* + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient( + private val baseUrl: String, + httpClientEngine: HttpClientEngine?, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + {{#gson}} + jsonBlock: GsonBuilder.() -> Unit = JSON_DEFAULT, + {{/gson}} + {{#jackson}} + jsonBlock: ObjectMapper.() -> Unit = JSON_DEFAULT, + {{/jackson}} +) { + + private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy { + { + it.install(ContentNegotiation) { + {{#gson}} + gson { jsonBlock() } + {{/gson}} + {{#jackson}} + jackson { jsonBlock() } + {{/jackson}} + } + httpClientConfig?.invoke(it) + } + } + + private val client: HttpClient by lazy { + httpClientEngine?.let { HttpClient(it, clientConfig) } ?: HttpClient(clientConfig) + } + + {{#hasAuthMethods}} + private val authentications: kotlin.collections.Map by lazy { + mapOf({{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + "{{name}}" to HttpBasicAuth(){{/isBasicBasic}}{{#isBasicBearer}} + "{{name}}" to HttpBearerAuth("{{scheme}}"){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + "{{name}}" to ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"){{/isApiKey}}{{#isOAuth}} + "{{name}}" to OAuth(){{/isOAuth}}{{^-last}}, {{/-last}}{{/authMethods}}) + } + {{/hasAuthMethods}} + {{^hasAuthMethods}} + private val authentications: kotlin.collections.Map? = null + {{/hasAuthMethods}} + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + const val BASE_URL = "{{{basePath}}}" + {{#gson}} + val JSON_DEFAULT : GsonBuilder.() -> Unit = { + setDateFormat(DateFormat.LONG) + setPrettyPrinting() + } + {{/gson}} + {{#jackson}} + val JSON_DEFAULT: ObjectMapper.() -> Unit = { + configure(SerializationFeature.INDENT_OUTPUT, true) + setDefaultPrettyPrinter(DefaultPrettyPrinter().apply { + indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance) + indentObjectsWith(DefaultIndenter(" ", "\n")) + }) + registerModule(JavaTimeModule()) + } + {{/jackson}} + protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType) + } + + /** + * Set the username for the first HTTP basic authentication. + * + * @param username Username + */ + fun setUsername(username: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? + ?: throw Exception("No HTTP basic authentication configured") + auth.username = username + } + + /** + * Set the password for the first HTTP basic authentication. + * + * @param password Password + */ + fun setPassword(password: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? + ?: throw Exception("No HTTP basic authentication configured") + auth.password = password + } + + /** + * Set the API key value for the first API key authentication. + * + * @param apiKey API key + * @param paramName The name of the API key parameter, or null or set the first key. + */ + fun setApiKey(apiKey: String, paramName: String? = null) { + val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth? + ?: throw Exception("No API key authentication configured") + auth.apiKey = apiKey + } + + /** + * Set the API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + * @param paramName The name of the API key parameter, or null or set the first key. + */ + fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) { + val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth? + ?: throw Exception("No API key authentication configured") + auth.apiKeyPrefix = apiKeyPrefix + } + + /** + * Set the access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + fun setAccessToken(accessToken: String) { + val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth? + ?: throw Exception("No OAuth2 authentication configured") + auth.accessToken = accessToken + } + + /** + * Set the access token for the first Bearer authentication. + * + * @param bearerToken The bearer token. + */ + fun setBearerToken(bearerToken: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth? + ?: throw Exception("No Bearer authentication configured") + auth.bearerToken = bearerToken + } + + protected suspend fun multipartFormRequest(requestConfig: RequestConfig, body: kotlin.collections.List?, authNames: kotlin.collections.List): HttpResponse { + return request(requestConfig, MultiPartFormDataContent(body ?: listOf()), authNames) + } + + protected suspend fun urlEncodedFormRequest(requestConfig: RequestConfig, body: Parameters?, authNames: kotlin.collections.List): HttpResponse { + return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames) + } + + protected suspend fun jsonRequest(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse = request(requestConfig, body, authNames) + + protected suspend fun request(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse { + requestConfig.updateForAuth(authNames) + val headers = requestConfig.headers + + return client.request { + this.url { + this.takeFrom(URLBuilder(baseUrl)) + appendPath(requestConfig.path.trimStart('/').split('/')) + requestConfig.query.forEach { query -> + query.value.forEach { value -> + parameter(query.key, value) + } + } + } + this.method = requestConfig.method.httpMethod + headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) } + if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) { + val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) } + ?: ContentType.Application.Json) + this.contentType(contentType) + setBody(body) + } + } + } + + private fun RequestConfig.updateForAuth(authNames: kotlin.collections.List) { + for (authName in authNames) { + val auth = authentications?.get(authName) ?: throw Exception("Authentication undefined: $authName") + auth.apply(query, headers) + } + } + + private fun URLBuilder.appendPath(components: kotlin.collections.List): URLBuilder = apply { + encodedPath = encodedPath.trimEnd('/') + components.joinToString("/", prefix = "/") { it.encodeURLQueryComponent() } + } + + private val RequestMethod.httpMethod: HttpMethod + get() = when (this) { + RequestMethod.DELETE -> HttpMethod.Delete + RequestMethod.GET -> HttpMethod.Get + RequestMethod.HEAD -> HttpMethod.Head + RequestMethod.PATCH -> HttpMethod.Patch + RequestMethod.PUT -> HttpMethod.Put + RequestMethod.POST -> HttpMethod.Post + RequestMethod.OPTIONS -> HttpMethod.Options + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache new file mode 100644 index 000000000000..87a68f3084e5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache @@ -0,0 +1,51 @@ +package {{packageName}}.infrastructure + +import io.ktor.http.Headers +import io.ktor.http.isSuccess +import io.ktor.util.reflect.TypeInfo +import io.ktor.util.reflect.typeInfo + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class HttpResponse(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider) { + val status: Int = response.status.value + val success: Boolean = response.status.isSuccess() + val headers: Map> = response.headers.mapEntries() + suspend fun body(): T = provider.body(response) + suspend fun typedBody(type: TypeInfo): V = provider.typedBody(response, type) + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private fun Headers.mapEntries(): Map> { + val result = mutableMapOf>() + entries().forEach { result[it.key] = it.value } + return result + } + } +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}interface BodyProvider { + suspend fun body(response: io.ktor.client.statement.HttpResponse): T + suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider(private val type: TypeInfo) : BodyProvider { + @Suppress("UNCHECKED_CAST") + override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = + response.call.body(type) as T + + @Suppress("UNCHECKED_CAST") + override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = + response.call.body(type) as V +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider(private val provider: BodyProvider, private val block: S.() -> T) : BodyProvider { + override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = + block(provider.body(response)) + + override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = + provider.typedBody(response, type) +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}inline fun io.ktor.client.statement.HttpResponse.wrap(): HttpResponse = + HttpResponse(this, TypedBodyProvider(typeInfo())) + +{{#nonPublicApi}}internal {{/nonPublicApi}}fun HttpResponse.map(block: T.() -> V): HttpResponse = + HttpResponse(response, MappedBodyProvider(provider, block)) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache new file mode 100644 index 000000000000..642794399125 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache @@ -0,0 +1,248 @@ +{{>licenseInfo}} +package {{apiPackage}} + +import java.io.IOException +import okhttp3.Call +import okhttp3.HttpUrl + +{{#imports}}import {{import}} +{{/imports}} + +{{^multiplatform}} +{{#gson}} +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +{{/moshi}} +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonProperty +{{/jackson}} +{{#kotlinx_serialization}} +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +{{/kotlinx_serialization}} +{{/multiplatform}} +{{#multiplatform}} +import kotlinx.serialization.* +{{/multiplatform}} + +{{^doNotUseRxAndCoroutines}} +{{#useCoroutines}} +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +{{/useCoroutines}} +{{/doNotUseRxAndCoroutines}} +import {{packageName}}.infrastructure.ApiClient +import {{packageName}}.infrastructure.ApiResponse +import {{packageName}}.infrastructure.ClientException +import {{packageName}}.infrastructure.ClientError +import {{packageName}}.infrastructure.ServerException +import {{packageName}}.infrastructure.ServerError +import {{packageName}}.infrastructure.MultiValueMap +import {{packageName}}.infrastructure.PartConfig +import {{packageName}}.infrastructure.RequestConfig +import {{packageName}}.infrastructure.RequestMethod +import {{packageName}}.infrastructure.ResponseType +import {{packageName}}.infrastructure.Success +import {{packageName}}.infrastructure.toMultiValue + +{{#operations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(basePath: kotlin.String = defaultBasePath, client: Call.Factory = ApiClient.defaultClient) : ApiClient(basePath, client) { + companion object { + @JvmStatic + val defaultBasePath: String by lazy { + System.getProperties().getProperty(ApiClient.baseUrlKey, "{{{basePath}}}") + } + } + + {{#operation}} + {{#allParams}} + {{#isEnum}} + /** + * enum for parameter {{paramName}} + */ + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{^enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^multiplatform}} + {{#moshi}} + @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/moshi}} + {{#gson}} + @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/multiplatform}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{#enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^multiplatform}} + {{#moshi}} + @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/moshi}} + {{#gson}} + @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{/multiplatform}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + + /** + * Override [toString()] to avoid using the enum variable name as the value, and instead use + * the actual value defined in the API spec file. + * + * This solves a problem when the variable name and its value are different, and ensures that + * the client sends the correct enum values to the server always. + */ + override fun toString(): kotlin.String = "$value" + } + + {{/isEnum}} + {{/allParams}} + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{#nullableReturnType}} or null{{/nullableReturnType}}{{/returnType}}{{^returnType}}void{{/returnType}} + * @throws IllegalStateException If the request is not correctly configured + * @throws IOException Rethrows the OkHttp execute method exception + * @throws UnsupportedOperationException If the API returns an informational or redirection response + * @throws ClientException If the API returns a client error response + * @throws ServerException If the API returns a server error response + */{{#returnType}} + @Suppress("UNCHECKED_CAST"){{/returnType}} + @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + {{^doNotUseRxAndCoroutines}}{{#useCoroutines}}suspend {{/useCoroutines}}{{/doNotUseRxAndCoroutines}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : {{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}{{^doNotUseRxAndCoroutines}}{{#useCoroutines}} = withContext(Dispatchers.IO){{/useCoroutines}}{{/doNotUseRxAndCoroutines}} { + {{#isDeprecated}} + @Suppress("DEPRECATION") + {{/isDeprecated}} + val localVarResponse = {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + + return{{^doNotUseRxAndCoroutines}}{{#useCoroutines}}@withContext{{/useCoroutines}}{{/doNotUseRxAndCoroutines}} when (localVarResponse.responseType) { + ResponseType.Success -> {{#returnType}}(localVarResponse as Success<*>).data as {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} + ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.") + ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.") + ResponseType.ClientError -> { + val localVarError = localVarResponse as ClientError<*> + throw ClientException("Client error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) + } + ResponseType.ServerError -> { + val localVarError = localVarResponse as ServerError<*> + throw ServerException("Server error : ${localVarError.statusCode} ${localVarError.message.orEmpty()} ${localVarError.body}", localVarError.statusCode, localVarResponse) + } + } + } + + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}}* @return ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}> + * @throws IllegalStateException If the request is not correctly configured + * @throws IOException Rethrows the OkHttp execute method exception + */{{#returnType}} + @Suppress("UNCHECKED_CAST"){{/returnType}} + @Throws(IllegalStateException::class, IOException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + {{^doNotUseRxAndCoroutines}}{{#useCoroutines}}suspend {{/useCoroutines}}{{/doNotUseRxAndCoroutines}}fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}>{{^doNotUseRxAndCoroutines}}{{#useCoroutines}} = withContext(Dispatchers.IO){{/useCoroutines}}{{/doNotUseRxAndCoroutines}} { + {{#isDeprecated}} + @Suppress("DEPRECATION") + {{/isDeprecated}} + val localVariableConfig = {{operationId}}RequestConfig({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + + return{{^doNotUseRxAndCoroutines}}{{#useCoroutines}}@withContext{{/useCoroutines}}{{/doNotUseRxAndCoroutines}} request<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}, {{{returnType}}}{{^returnType}}Unit{{/returnType}}>( + localVariableConfig + ) + } + + /** + * To obtain the request config of the operation {{operationId}} + * + {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}}* @return RequestConfig + */ + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}RequestConfig({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : RequestConfig<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}> { + val localVariableBody = {{#hasBodyParam}}{{! + }}{{#bodyParams}}{{{paramName}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{! + }}{{^hasFormParams}}null{{/hasFormParams}}{{! + }}{{#hasFormParams}}mapOf({{#formParams}} + "{{#lambda.escapeDollar}}{{{baseName}}}{{/lambda.escapeDollar}}" to PartConfig(body = {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}, headers = mutableMapOf({{#contentType}}"Content-Type" to "{{contentType}}"{{/contentType}})),{{! + }}{{/formParams}}){{/hasFormParams}}{{! + }}{{/hasBodyParam}} + val localVariableQuery: MultiValueMap = {{^hasQueryParams}}mutableMapOf() +{{/hasQueryParams}}{{#hasQueryParams}}mutableMapOf>() + .apply { + {{#queryParams}} + {{^required}} + if ({{{paramName}}} != null) { + put("{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.value{{/isString}}{{^isString}}{{{paramName}}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}}) + } + {{/required}} + {{#required}} + {{#isNullable}} + if ({{{paramName}}} != null) { + put("{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.value{{/isString}}{{^isString}}{{{paramName}}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}}) + } + {{/isNullable}} + {{^isNullable}} + put("{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.value{{/isString}}{{^isString}}{{{paramName}}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}}) + {{/isNullable}} + {{/required}} + {{/queryParams}} + } + {{/hasQueryParams}} + val localVariableHeaders: MutableMap = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}}) + {{#headerParams}} + {{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } + {{/headerParams}} + {{^hasFormParams}}{{#hasConsumes}}{{#consumes}}localVariableHeaders["Content-Type"] = "{{{mediaType}}}" + {{/consumes}}{{/hasConsumes}}{{/hasFormParams}}{{#hasProduces}}localVariableHeaders["Accept"] = "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}" +{{/hasProduces}} + + return RequestConfig( + method = RequestMethod.{{httpMethod}}, + path = "{{path}}"{{#pathParams}}.replace("{"+"{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"+"}", encodeURIComponent({{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}.toString(){{/isContainer}})){{/pathParams}}, + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, + body = localVariableBody + ) + } + + {{/operation}} + + private fun encodeURIComponent(uriComponent: kotlin.String): kotlin.String = + HttpUrl.Builder().scheme("http").host("localhost").addPathSegment(uriComponent).build().encodedPathSegments[0] +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..8aabf649f883 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,448 @@ +package {{packageName}}.infrastructure + +{{#supportAndroidApiLevel25AndBelow}} +import android.os.Build +{{/supportAndroidApiLevel25AndBelow}} +import okhttp3.OkHttpClient +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.asRequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.FormBody +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.ResponseBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.Headers +import okhttp3.Headers.Companion.toHeaders +import okhttp3.MultipartBody +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Response +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.io.IOException +import java.net.URLConnection +{{^threetenbp}} +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.OffsetTime +{{/threetenbp}} +import java.util.Locale +import java.util.regex.Pattern +{{#useCoroutines}} +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlinx.coroutines.suspendCancellableCoroutine +{{/useCoroutines}} +{{#kotlinx_serialization}} +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +{{/kotlinx_serialization}} +{{#threetenbp}} +import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.LocalTime +import org.threeten.bp.OffsetDateTime +import org.threeten.bp.OffsetTime +{{/threetenbp}} +{{#gson}} +import com.google.gson.reflect.TypeToken +{{/gson}} +{{#jackson}} +import com.fasterxml.jackson.core.type.TypeReference +{{/jackson}} +{{#moshi}} +import com.squareup.moshi.adapter +{{/moshi}} + +{{#nonPublicApi}}internal {{/nonPublicApi}}val EMPTY_REQUEST: RequestBody = ByteArray(0).toRequestBody() + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(val baseUrl: String, val client: Call.Factory = defaultClient) { + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + protected const val ContentType = "Content-Type" + protected const val Accept = "Accept" + protected const val Authorization = "Authorization" + protected const val JsonMediaType = "application/json" + protected const val FormDataMediaType = "multipart/form-data" + protected const val FormUrlEncMediaType = "application/x-www-form-urlencoded" + protected const val XmlMediaType = "application/xml" + protected const val OctetMediaType = "application/octet-stream" + + val apiKey: MutableMap = mutableMapOf() + val apiKeyPrefix: MutableMap = mutableMapOf() + var username: String? = null + var password: String? = null + var accessToken: String? = null + const val baseUrlKey = "{{packageName}}.baseUrl" + + @JvmStatic + val defaultClient: OkHttpClient by lazy { + builder.build() + } + + @JvmStatic + val builder: OkHttpClient.Builder = OkHttpClient.Builder() + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + protected fun guessContentTypeFromFile(file: File): String { + val contentType = URLConnection.guessContentTypeFromName(file.name) + return contentType ?: "application/octet-stream" + } + + protected inline fun requestBody(content: T, mediaType: String?): RequestBody = + when { + content is File -> content.asRequestBody((mediaType ?: guessContentTypeFromFile(content)).toMediaTypeOrNull()) + mediaType == FormDataMediaType -> + MultipartBody.Builder() + .setType(MultipartBody.FORM) + .apply { + // content's type *must* be Map> + @Suppress("UNCHECKED_CAST") + (content as Map>).forEach { (name, part) -> + if (part.body is File) { + val partHeaders = part.headers.toMutableMap() + + ("Content-Disposition" to "form-data; name=\"$name\"; filename=\"${part.body.name}\"") + val fileMediaType = guessContentTypeFromFile(part.body).toMediaTypeOrNull() + addPart( + partHeaders.toHeaders(), + part.body.asRequestBody(fileMediaType) + ) + } else { + val partHeaders = part.headers.toMutableMap() + + ("Content-Disposition" to "form-data; name=\"$name\"") + addPart( + partHeaders.toHeaders(), + parameterToString(part.body).toRequestBody(null) + ) + } + } + }.build() + mediaType == FormUrlEncMediaType -> { + FormBody.Builder().apply { + // content's type *must* be Map> + @Suppress("UNCHECKED_CAST") + (content as Map>).forEach { (name, part) -> + add(name, parameterToString(part.body)) + } + }.build() + } + mediaType == null || mediaType.startsWith("application/") && mediaType.endsWith("json") -> + if (content == null) { + EMPTY_REQUEST + } else { + {{#moshi}} + Serializer.moshi.adapter(T::class.java).toJson(content) + {{/moshi}} + {{#gson}} + Serializer.gson.toJson(content, T::class.java) + {{/gson}} + {{#jackson}} + Serializer.jacksonObjectMapper.writeValueAsString(content) + {{/jackson}} + {{#kotlinx_serialization}} + Serializer.kotlinxSerializationJson.encodeToString(content) + {{/kotlinx_serialization}} + .toRequestBody((mediaType ?: JsonMediaType).toMediaTypeOrNull()) + } + mediaType == XmlMediaType -> throw UnsupportedOperationException("xml not currently supported.") + mediaType == OctetMediaType && content is ByteArray -> + content.toRequestBody(OctetMediaType.toMediaTypeOrNull()) + // TODO: this should be extended with other serializers + else -> throw UnsupportedOperationException("requestBody currently only supports JSON body, byte body and File body.") + } + + {{#moshi}} + @OptIn(ExperimentalStdlibApi::class) + {{/moshi}} + protected inline fun responseBody(response: Response, mediaType: String? = JsonMediaType): T? { + val body = response.body + if(body == null) { + return null + } + if (T::class.java == File::class.java) { + // return tempFile + val contentDisposition = response.header("Content-Disposition") + + val fileName = if (contentDisposition != null) { + // Get filename from the Content-Disposition header. + val pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?") + val matcher = pattern.matcher(contentDisposition) + if (matcher.find()) { + matcher.group(1) + ?.replace(".*[/\\\\]", "") + ?.replace(";", "") + } else { + null + } + } else { + null + } + + var prefix: String? + val suffix: String? + if (fileName == null) { + prefix = "download" + suffix = "" + } else { + val pos = fileName.lastIndexOf(".") + if (pos == -1) { + prefix = fileName + suffix = null + } else { + prefix = fileName.substring(0, pos) + suffix = fileName.substring(pos) + } + // Files.createTempFile requires the prefix to be at least three characters long + if (prefix.length < 3) { + prefix = "download" + } + } + + {{^supportAndroidApiLevel25AndBelow}} + // Attention: if you are developing an android app that supports API Level 25 and bellow, please check flag supportAndroidApiLevel25AndBelow in https://openapi-generator.tech/docs/generators/kotlin#config-options + val tempFile = java.nio.file.Files.createTempFile(prefix, suffix).toFile() + {{/supportAndroidApiLevel25AndBelow}} + {{#supportAndroidApiLevel25AndBelow}} + val tempFile = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + java.nio.file.Files.createTempFile(prefix, suffix).toFile() + } else { + @Suppress("DEPRECATION") + createTempFile(prefix, suffix) + } + {{/supportAndroidApiLevel25AndBelow}} + tempFile.deleteOnExit() + body.byteStream().use { inputStream -> + tempFile.outputStream().use { tempFileOutputStream -> + inputStream.copyTo(tempFileOutputStream) + } + } + return tempFile as T + } + + return when { + mediaType == null || (mediaType.startsWith("application/") && mediaType.endsWith("json")) -> { + val bodyContent = body.string() + if (bodyContent.isEmpty()) { + return null + } + {{#moshi}}Serializer.moshi.adapter().fromJson(bodyContent){{/moshi}}{{! + }}{{#gson}}Serializer.gson.fromJson(bodyContent, (object: TypeToken(){}).getType()){{/gson}}{{! + }}{{#jackson}}Serializer.jacksonObjectMapper.readValue(bodyContent, object: TypeReference() {}){{/jackson}}{{! + }}{{#kotlinx_serialization}}Serializer.kotlinxSerializationJson.decodeFromString(bodyContent){{/kotlinx_serialization}} + } + mediaType == OctetMediaType -> body.bytes() as? T + else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.") + } + } + + {{#hasAuthMethods}} + protected fun updateAuthParams(requestConfig: RequestConfig) { + {{#authMethods}} + {{#isApiKey}} + {{#isKeyInHeader}} + if (requestConfig.headers["{{keyParamName}}"].isNullOrEmpty()) { + {{/isKeyInHeader}} + {{#isKeyInQuery}} + if (requestConfig.query["{{keyParamName}}"].isNullOrEmpty()) { + {{/isKeyInQuery}} + if (apiKey["{{keyParamName}}"] != null) { + if (apiKeyPrefix["{{keyParamName}}"] != null) { + {{#isKeyInHeader}} + requestConfig.headers["{{keyParamName}}"] = apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!! + {{/isKeyInHeader}} + {{#isKeyInQuery}} + requestConfig.query["{{keyParamName}}"] = listOf(apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!) + {{/isKeyInQuery}} + } else { + {{#isKeyInHeader}} + requestConfig.headers["{{keyParamName}}"] = apiKey["{{keyParamName}}"]!! + {{/isKeyInHeader}} + {{#isKeyInQuery}} + requestConfig.query["{{keyParamName}}"] = listOf(apiKey["{{keyParamName}}"]!!) + {{/isKeyInQuery}} + } + } + {{#isKeyInQuery}} + } + {{/isKeyInQuery}} + {{#isKeyInHeader}} + } + {{/isKeyInHeader}} + {{/isApiKey}} + {{#isBasic}} + {{#isBasicBasic}} + if (requestConfig.headers[Authorization].isNullOrEmpty()) { + username?.let { username -> + password?.let { password -> + requestConfig.headers[Authorization] = okhttp3.Credentials.basic(username, password) + } + } + } + {{/isBasicBasic}} + {{#isBasicBearer}} + if (requestConfig.headers[Authorization].isNullOrEmpty()) { + accessToken?.let { accessToken -> + requestConfig.headers[Authorization] = "Bearer $accessToken" + } + } + {{/isBasicBearer}} + {{/isBasic}} + {{#isOAuth}} + if (requestConfig.headers[Authorization].isNullOrEmpty()) { + accessToken?.let { accessToken -> + requestConfig.headers[Authorization] = "Bearer $accessToken " + } + } + {{/isOAuth}} + {{/authMethods}} + } + {{/hasAuthMethods}} + + protected {{#useCoroutines}}suspend {{/useCoroutines}}inline fun request(requestConfig: RequestConfig): ApiResponse { + val httpUrl = baseUrl.toHttpUrlOrNull() ?: throw IllegalStateException("baseUrl is invalid.") + {{#hasAuthMethods}} + + // take authMethod from operation + updateAuthParams(requestConfig) + {{/hasAuthMethods}} + + val url = httpUrl.newBuilder() + .addEncodedPathSegments(requestConfig.path.trimStart('/')) + .apply { + requestConfig.query.forEach { query -> + query.value.forEach { queryValue -> + addQueryParameter(query.key, queryValue) + } + } + }.build() + + // take content-type/accept from spec or set to default (application/json) if not defined + if (requestConfig.body != null && requestConfig.headers[ContentType].isNullOrEmpty()) { + requestConfig.headers[ContentType] = JsonMediaType + } + if (requestConfig.headers[Accept].isNullOrEmpty()) { + requestConfig.headers[Accept] = JsonMediaType + } + val headers = requestConfig.headers + + if (headers[Accept].isNullOrEmpty()) { + throw kotlin.IllegalStateException("Missing Accept header. This is required.") + } + + val contentType = if (headers[ContentType] != null) { + // TODO: support multiple contentType options here. + (headers[ContentType] as String).substringBefore(";").lowercase(Locale.US) + } else { + null + } + + val request = when (requestConfig.method) { + RequestMethod.DELETE -> Request.Builder().url(url).delete(requestBody(requestConfig.body, contentType)) + RequestMethod.GET -> Request.Builder().url(url) + RequestMethod.HEAD -> Request.Builder().url(url).head() + RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(requestConfig.body, contentType)) + RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(requestConfig.body, contentType)) + RequestMethod.POST -> Request.Builder().url(url).post(requestBody(requestConfig.body, contentType)) + RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null) + }.apply { + headers.forEach { header -> addHeader(header.key, header.value) } + }.build() + + {{#useCoroutines}} + val response: Response = suspendCancellableCoroutine { continuation -> + val call = client.newCall(request) + continuation.invokeOnCancellation { call.cancel() } + call.enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + continuation.resumeWithException(e) + } + override fun onResponse(call: Call, response: Response) { + continuation.resume(response) + } + }) + } + {{/useCoroutines}} + {{^useCoroutines}} + val response = client.newCall(request).execute() + {{/useCoroutines}} + + val accept = response.header(ContentType)?.substringBefore(";")?.lowercase(Locale.US) + + // TODO: handle specific mapping types. e.g. Map> + @Suppress("UNNECESSARY_SAFE_CALL") + return response.use { + when { + it.isRedirect -> Redirection( + it.code, + it.headers.toMultimap() + ) + it.isInformational -> Informational( + it.message, + it.code, + it.headers.toMultimap() + ) + it.isSuccessful -> Success( + responseBody(it, accept), + it.code, + it.headers.toMultimap() + ) + it.isClientError -> ClientError( + it.message, + it.body?.string(), + it.code, + it.headers.toMultimap() + ) + else -> ServerError( + it.message, + it.body?.string(), + it.code, + it.headers.toMultimap() + ) + } + } + } + + protected fun parameterToString(value: Any?): String = when (value) { + null -> "" + is Array<*> -> toMultiValue(value, "csv").toString() + is Iterable<*> -> toMultiValue(value, "csv").toString() + is OffsetDateTime, is OffsetTime, is LocalDateTime, is LocalDate, is LocalTime -> + parseDateToQueryString(value) + else -> value.toString() + } + + protected inline fun parseDateToQueryString(value : T): String { + {{#toJson}} + /* + .replace("\"", "") converts the json object string to an actual string for the query parameter. + The moshi or gson adapter allows a more generic solution instead of trying to use a native + formatter. It also easily allows to provide a simple way to define a custom date format pattern + inside a gson/moshi adapter. + */ + {{#moshi}} + return Serializer.moshi.adapter(T::class.java).toJson(value).replace("\"", "") + {{/moshi}} + {{#gson}} + return Serializer.gson.toJson(value, T::class.java).replace("\"", "") + {{/gson}} + {{#jackson}} + return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") + {{/jackson}} + {{#kotlinx_serialization}} + return Serializer.kotlinxSerializationJson.encodeToString(value).replace("\"", "") + {{/kotlinx_serialization}} + {{/toJson}} + {{^toJson}} + return value.toString() + {{/toJson}} + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache new file mode 100644 index 000000000000..d529ad5599f1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache @@ -0,0 +1,43 @@ +package {{packageName}}.infrastructure + +{{#nonPublicApi}}internal {{/nonPublicApi}}enum class ResponseType { + Success, Informational, Redirection, ClientError, ServerError +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}interface Response + +{{#nonPublicApi}}internal {{/nonPublicApi}}abstract class ApiResponse(val responseType: ResponseType): Response { + abstract val statusCode: Int + abstract val headers: Map> +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class Success( + val data: T{{#nullableReturnType}}?{{/nullableReturnType}}, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +): ApiResponse(ResponseType.Success) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class Informational( + val statusText: String, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiResponse(ResponseType.Informational) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class Redirection( + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiResponse(ResponseType.Redirection) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class ClientError( + val message: String? = null, + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiResponse(ResponseType.ClientError) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class ServerError( + val message: String? = null, + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> +): ApiResponse(ResponseType.ServerError) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache new file mode 100644 index 000000000000..1357da8d599c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache @@ -0,0 +1,18 @@ +@file:Suppress("unused") +package {{packageName}}.infrastructure + +import java.lang.RuntimeException + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ClientException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private const val serialVersionUID: Long = 123L + } +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ServerException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private const val serialVersionUID: Long = 456L + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache new file mode 100644 index 000000000000..8bf8d157f6c2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache @@ -0,0 +1,24 @@ +package {{packageName}}.infrastructure + +import okhttp3.Response + +/** + * Provides an extension to evaluation whether the response is a 1xx code + */ +{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isInformational : Boolean get() = this.code in 100..199 + +/** + * Provides an extension to evaluation whether the response is a 3xx code + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isRedirect : Boolean get() = this.code in 300..399 + +/** + * Provides an extension to evaluation whether the response is a 4xx code + */ +{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isClientError : Boolean get() = this.code in 400..499 + +/** + * Provides an extension to evaluation whether the response is a 5xx (Standard) through 999 (non-standard) code + */ +{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isServerError : Boolean get() = this.code in 500..999 diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache new file mode 100644 index 000000000000..4381539414cd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache @@ -0,0 +1,161 @@ +package {{apiPackage}} + +import {{packageName}}.infrastructure.CollectionFormats.* +import retrofit2.http.* +{{#doNotUseRxAndCoroutines}} +import retrofit2.Call +{{/doNotUseRxAndCoroutines}} +{{^doNotUseRxAndCoroutines}} +{{#useCoroutines}} +import retrofit2.Response +{{/useCoroutines}} +{{/doNotUseRxAndCoroutines}} +import okhttp3.RequestBody +{{#isResponseFile}} +import okhttp3.ResponseBody +{{/isResponseFile}} +{{#isMultipart}} +import okhttp3.MultipartBody +{{/isMultipart}} +{{^doNotUseRxAndCoroutines}} +{{#useRxJava}} +import rx.Observable +{{/useRxJava}} +{{#useRxJava2}} +import io.reactivex.Single +{{/useRxJava2}} +{{#useRxJava3}} +import io.reactivex.rxjava3.core.Single +{{/useRxJava3}} +{{^returnType}} +{{#useRxJava2}} +import io.reactivex.Completable +{{/useRxJava2}} +{{#useRxJava3}} +import io.reactivex.rxjava3.core.Completable +{{/useRxJava3}} +{{/returnType}} +{{/doNotUseRxAndCoroutines}} +{{^multiplatform}} +{{#gson}} +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +{{/moshi}} +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonProperty +{{/jackson}} +{{#kotlinx_serialization}} +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +{{/kotlinx_serialization}} +{{/multiplatform}} +{{#multiplatform}} +import kotlinx.serialization.* +{{/multiplatform}} + +{{#imports}}import {{import}} +{{/imports}} + +{{#operations}} +{{#x-kotlin-import-models}} +import {{{modelPackage}}}.* + +{{/x-kotlin-import-models}} +{{#x-kotlin-multipart-import}} +{{^isMultipart}} +import okhttp3.MultipartBody + +{{/isMultipart}} +{{/x-kotlin-multipart-import}} +{{#nonPublicApi}}internal {{/nonPublicApi}}interface {{classname}} { + {{#operation}} + {{#allParams}} + {{#isEnum}} + + /** + * enum for parameter {{paramName}} + */ + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{^enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^multiplatform}} + {{#moshi}} + @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/moshi}} + {{#gson}} + @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/multiplatform}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{#enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^-last}} + {{^multiplatform}} + {{#moshi}} + @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/moshi}} + {{#gson}} + @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/kotlinx_serialization}} + {{/multiplatform}} + {{#multiplatform}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/multiplatform}} + {{/-last}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + } + + {{/isEnum}} + {{/allParams}} + /** + * {{summary}} + * {{notes}} + * Responses:{{#responses}} + * - {{code}}: {{{message}}}{{/responses}} + *{{>paramJavadoc}} + * @return {{^useCoroutines}}[Call]<{{/useCoroutines}}{{#isResponseFile}}[ResponseBody]{{/isResponseFile}}{{^isResponseFile}}{{#returnType}}[{{{.}}}]{{/returnType}}{{^returnType}}[Unit]{{/returnType}}{{/isResponseFile}}{{^useCoroutines}}>{{/useCoroutines}} + */ + {{#isDeprecated}} + @Deprecated("This api was deprecated") + {{/isDeprecated}} + {{#formParams}} + {{#-first}} + {{#isMultipart}}@Multipart{{/isMultipart}}{{^isMultipart}}@FormUrlEncoded{{/isMultipart}} + {{/-first}} + {{/formParams}} + {{^formParams}} + {{#prioritizedContentTypes}} + {{#-first}} + @Headers("Content-Type:{{{mediaType}}}") + {{/-first}} + {{/prioritizedContentTypes}} + {{/formParams}} + @{{httpMethod}}("{{{path}}}") + {{^doNotUseRxAndCoroutines}}{{#useCoroutines}}suspend {{/useCoroutines}}{{/doNotUseRxAndCoroutines}}fun {{operationId}}({{^allParams}}){{/allParams}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/allParams}}: {{^doNotUseRxAndCoroutines}}{{#useRxJava}}Observable<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isResponseFile}}>{{/useRxJava}}{{#useRxJava2}}{{#returnType}}Single<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}>{{/returnType}}{{^returnType}}Completable{{/returnType}}{{/useRxJava2}}{{#useRxJava3}}{{#returnType}}Single<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}>{{/returnType}}{{^returnType}}Completable{{/returnType}}{{/useRxJava3}}{{#useCoroutines}}Response<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isResponseFile}}>{{/useCoroutines}}{{/doNotUseRxAndCoroutines}}{{#doNotUseRxAndCoroutines}}Call<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isResponseFile}}>{{/doNotUseRxAndCoroutines}} + + {{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache new file mode 100644 index 000000000000..f764206df126 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache @@ -0,0 +1,88 @@ +# {{classname}}{{#description}} +{{.}}{{/description}} + +All URIs are relative to *{{basePath}}* + +| Method | HTTP request | Description | +| ------------- | ------------- | ------------- | +{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} | +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +{{summary}}{{#notes}} + +{{.}}{{/notes}} + +### Example +```kotlin +// Import classes: +//import {{{packageName}}}.* +//import {{{packageName}}}.infrastructure.* +//import {{{modelPackage}}}.* + +val apiClient = ApiClient() +{{#authMethods}} +{{#isBasic}} +{{#isBasicBasic}} +apiClient.setCredentials("USERNAME", "PASSWORD") +{{/isBasicBasic}} +{{#isBasicBearer}} +apiClient.setBearerToken("TOKEN") +{{/isBasicBearer}} +{{/isBasic}} +{{/authMethods}} +val webService = apiClient.createWebservice({{{classname}}}::class.java) +{{#allParams}} +val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} +{{/allParams}} + +{{#useCoroutines}} +launch(Dispatchers.IO) { +{{/useCoroutines}} +{{#useCoroutines}} {{/useCoroutines}}{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}webService.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) +{{#useCoroutines}} +} +{{/useCoroutines}} +``` + +### Parameters +{{^allParams}} +This endpoint does not need any parameter. +{{/allParams}} +{{#allParams}} +{{#-last}} +| Name | Type | Description | Notes | +| ------------- | ------------- | ------------- | ------------- | +{{/-last}} +| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} | +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}} +{{#authMethods}} +{{#isBasic}} +{{#isBasicBasic}} +Configure {{name}}: + ApiClient().setCredentials("USERNAME", "PASSWORD") +{{/isBasicBasic}} +{{#isBasicBearer}} +Configure {{name}}: + ApiClient().setBearerToken("TOKEN") +{{/isBasicBearer}} +{{/isBasic}} +{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache new file mode 100644 index 000000000000..967980ae7fb5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache @@ -0,0 +1,50 @@ +package {{packageName}}.auth + +import java.io.IOException +import java.net.URI +import java.net.URISyntaxException + +import okhttp3.Interceptor +import okhttp3.Response + +class ApiKeyAuth( + private val location: String = "", + private val paramName: String = "", + private var apiKey: String = "" +) : Interceptor { + + @Throws(IOException::class) + override fun intercept(chain: Interceptor.Chain): Response { + var request = chain.request() + + if ("query" == location) { + var newQuery = request.url.toUri().query + val paramValue = "$paramName=$apiKey" + if (newQuery == null) { + newQuery = paramValue + } else { + newQuery += "&$paramValue" + } + + val newUri: URI + try { + val oldUri = request.url.toUri() + newUri = URI(oldUri.scheme, oldUri.authority, + oldUri.path, newQuery, oldUri.fragment) + } catch (e: URISyntaxException) { + throw IOException(e) + } + + request = request.newBuilder().url(newUri.toURL()).build() + } else if ("header" == location) { + request = request.newBuilder() + .addHeader(paramName, apiKey) + .build() + } else if ("cookie" == location) { + request = request.newBuilder() + .addHeader("Cookie", "$paramName=$apiKey") + .build() + } + return chain.proceed(request) + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache new file mode 100644 index 000000000000..fc122514388c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache @@ -0,0 +1,33 @@ +package {{packageName}}.auth + +import java.io.IOException + +import kotlin.jvm.Throws +import okhttp3.Interceptor +import okhttp3.Interceptor.Chain +import okhttp3.Response +import okhttp3.Credentials + +class HttpBasicAuth( + private var username: String = "", + private var password: String = "" +) : Interceptor { + + fun setCredentials(username: String, password: String) { + this.username = username + this.password = password + } + + @Throws(IOException::class) + override fun intercept(chain: Chain): Response { + var request = chain.request() + + // If the request already have an authorization (eg. Basic auth), do nothing + if (request.header("Authorization") == null && username.isNotBlank() && password.isNotBlank()) { + request = request.newBuilder() + .addHeader("Authorization", Credentials.basic(username, password)) + .build() + } + return chain.proceed(request) + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache new file mode 100644 index 000000000000..7488e6a9f573 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache @@ -0,0 +1,39 @@ +package {{packageName}}.auth + +import java.io.IOException + +import okhttp3.Interceptor +import okhttp3.Interceptor.Chain +import okhttp3.Response + +class HttpBearerAuth( + private var schema: String = "", + var bearerToken: String = "" +) : Interceptor { + + @Throws(IOException::class) + override fun intercept(chain: Chain): Response { + var request = chain.request() + + // If the request already have an authorization (eg. Basic auth), do nothing + if (request.header("Authorization") == null && bearerToken.isNotBlank()) { + request = request.newBuilder() + .addHeader("Authorization", headerValue()) + .build() + } + return chain.proceed(request) + } + + private fun headerValue(): String { + return if (schema.isNotBlank()) { + "${upperCaseBearer()} $bearerToken" + } else { + bearerToken + } + } + + private fun upperCaseBearer(): String { + return if (schema.lowercase().equals("bearer")) "Bearer" else schema + } + +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache new file mode 100644 index 000000000000..8063b6c71861 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache @@ -0,0 +1,151 @@ +package {{packageName}}.auth + +import java.net.HttpURLConnection.HTTP_UNAUTHORIZED +import java.net.HttpURLConnection.HTTP_FORBIDDEN + +import java.io.IOException + +import org.apache.oltu.oauth2.client.OAuthClient +import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest +import org.apache.oltu.oauth2.client.request.OAuthClientRequest +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder +import org.apache.oltu.oauth2.common.exception.OAuthProblemException +import org.apache.oltu.oauth2.common.exception.OAuthSystemException +import org.apache.oltu.oauth2.common.message.types.GrantType +import org.apache.oltu.oauth2.common.token.BasicOAuthToken + +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Response + +class OAuth( + client: OkHttpClient, + var tokenRequestBuilder: TokenRequestBuilder +) : Interceptor { + + interface AccessTokenListener { + fun notify(token: BasicOAuthToken) + } + + private var oauthClient: OAuthClient = OAuthClient(OAuthOkHttpClient(client)) + + @Volatile + private var accessToken: String? = null + var authenticationRequestBuilder: AuthenticationRequestBuilder? = null + private var accessTokenListener: AccessTokenListener? = null + + constructor( + requestBuilder: TokenRequestBuilder + ) : this( + OkHttpClient(), + requestBuilder + ) + + constructor( + flow: OAuthFlow, + authorizationUrl: String, + tokenUrl: String, + scopes: String + ) : this( + OAuthClientRequest.tokenLocation(tokenUrl).setScope(scopes) + ) { + setFlow(flow) + authenticationRequestBuilder = OAuthClientRequest.authorizationLocation(authorizationUrl) + } + + fun setFlow(flow: OAuthFlow) { + when (flow) { + OAuthFlow.accessCode, OAuthFlow.implicit -> + tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE) + OAuthFlow.password -> + tokenRequestBuilder.setGrantType(GrantType.PASSWORD) + OAuthFlow.application -> + tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS) + } + } + + @Throws(IOException::class) + override fun intercept(chain: Interceptor.Chain): Response { + return retryingIntercept(chain, true) + } + + @Throws(IOException::class) + private fun retryingIntercept(chain: Interceptor.Chain, updateTokenAndRetryOnAuthorizationFailure: Boolean): Response { + var request = chain.request() + + // If the request already have an authorization (eg. Basic auth), do nothing + if (request.header("Authorization") != null) { + return chain.proceed(request) + } + + // If first time, get the token + val oAuthRequest: OAuthClientRequest + if (accessToken == null) { + updateAccessToken(null) + } + + if (accessToken != null) { + // Build the request + val rb = request.newBuilder() + + val requestAccessToken = accessToken + try { + oAuthRequest = OAuthBearerClientRequest(request.url.toString()) + .setAccessToken(requestAccessToken) + .buildHeaderMessage() + } catch (e: OAuthSystemException) { + throw IOException(e) + } + + oAuthRequest.headers.entries.forEach { header -> + rb.addHeader(header.key, header.value) + } + rb.url(oAuthRequest.locationUri) + + //Execute the request + val response = chain.proceed(rb.build()) + + // 401/403 most likely indicates that access token has expired. Unless it happens two times in a row. + if ((response.code == HTTP_UNAUTHORIZED || response.code == HTTP_FORBIDDEN) && updateTokenAndRetryOnAuthorizationFailure) { + try { + if (updateAccessToken(requestAccessToken)) { + response.body?.close() + return retryingIntercept(chain, false) + } + } catch (e: Exception) { + response.body?.close() + throw e + } + } + return response + } else { + return chain.proceed(chain.request()) + } + } + + /** + * Returns true if the access token has been updated + */ + @Throws(IOException::class) + @Synchronized + fun updateAccessToken(requestAccessToken: String?): Boolean { + if (accessToken == null || accessToken.equals(requestAccessToken)) { + return try { + val accessTokenResponse = oauthClient.accessToken(this.tokenRequestBuilder.buildBodyMessage()) + if (accessTokenResponse != null && accessTokenResponse.accessToken != null) { + accessToken = accessTokenResponse.accessToken + accessTokenListener?.notify(accessTokenResponse.oAuthToken as BasicOAuthToken) + !accessToken.equals(requestAccessToken) + } else { + false + } + } catch (e: OAuthSystemException) { + throw IOException(e) + } catch (e: OAuthProblemException) { + throw IOException(e) + } + } + return true + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache new file mode 100644 index 000000000000..05ad3f7e547b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache @@ -0,0 +1,5 @@ +package {{packageName}}.auth + +enum class OAuthFlow { + accessCode, implicit, password, application +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache new file mode 100644 index 000000000000..0731e92a890c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache @@ -0,0 +1,61 @@ +package {{packageName}}.auth + +import java.io.IOException + +import org.apache.oltu.oauth2.client.HttpClient +import org.apache.oltu.oauth2.client.request.OAuthClientRequest +import org.apache.oltu.oauth2.client.response.OAuthClientResponse +import org.apache.oltu.oauth2.client.response.OAuthClientResponseFactory +import org.apache.oltu.oauth2.common.exception.OAuthProblemException +import org.apache.oltu.oauth2.common.exception.OAuthSystemException + +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody + + +class OAuthOkHttpClient( + private var client: OkHttpClient = OkHttpClient() +) : HttpClient { + + @Throws(OAuthSystemException::class, OAuthProblemException::class) + override fun execute( + request: OAuthClientRequest, + headers: Map?, + requestMethod: String, + responseClass: Class?): T { + + var mediaType = "application/json".toMediaTypeOrNull() + val requestBuilder = Request.Builder().url(request.locationUri) + + headers?.forEach { entry -> + if (entry.key.equals("Content-Type", true)) { + mediaType = entry.value.toMediaTypeOrNull() + } else { + requestBuilder.addHeader(entry.key, entry.value) + } + } + + val body: RequestBody? = if (request.body != null) request.body.toRequestBody(mediaType) else null + requestBuilder.method(requestMethod, body) + + try { + val response = client.newCall(requestBuilder.build()).execute() + return OAuthClientResponseFactory.createCustomResponse( + response.body?.string(), + response.body?.contentType()?.toString(), + response.code, + response.headers.toMultimap(), + responseClass) + } catch (e: IOException) { + throw OAuthSystemException(e) + } + } + + override fun shutdown() { + // Nothing to do here + } + +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache new file mode 100644 index 000000000000..15ec80cdbadd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache @@ -0,0 +1 @@ +{{#isBodyParam}}@Body {{{paramName}}}: {{{dataType}}}{{^required}}? = null{{/required}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache new file mode 100644 index 000000000000..3aaa4e67fbf4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache @@ -0,0 +1 @@ +@Query("{{baseName}}") {{{baseName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}{{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{{dataType}}}{{/collectionFormat}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache new file mode 100644 index 000000000000..fe5c9db69053 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache @@ -0,0 +1 @@ +{{#isFormParam}}{{^isFile}}{{#isMultipart}}@Part{{/isMultipart}}{{^isMultipart}}@Field{{/isMultipart}}("{{baseName}}") {{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isFile}}{{#isFile}}{{#isMultipart}}@Part{{/isMultipart}}{{^isMultipart}}@Field("{{baseName}}"){{/isMultipart}} {{{paramName}}}: {{#isCollectionFormatMulti}}List<{{/isCollectionFormatMulti}}MultipartBody.Part{{#isCollectionFormatMulti}}>{{/isCollectionFormatMulti}}{{^required}}? = null{{/required}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache new file mode 100644 index 000000000000..53f81b071750 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache @@ -0,0 +1 @@ +{{#isHeaderParam}}@Header("{{baseName}}") {{{paramName}}}: {{#isEnum}}{{enumName}}{{operationIdCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..a10257b657f5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,412 @@ +package {{packageName}}.infrastructure + +{{#hasOAuthMethods}} +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder +import {{packageName}}.auth.OAuth +import {{packageName}}.auth.OAuth.AccessTokenListener +import {{packageName}}.auth.OAuthFlow +{{/hasOAuthMethods}} +{{#hasAuthMethods}} +{{#authMethods}} +{{#isBasic}} +{{#isBasicBasic}} +import {{packageName}}.auth.HttpBasicAuth +{{/isBasicBasic}} +{{#isBasicBearer}} +import {{packageName}}.auth.HttpBearerAuth +{{/isBasicBearer}} +{{/isBasic}} +{{#isApiKey}} +import {{packageName}}.auth.ApiKeyAuth +{{/isApiKey}} +{{/authMethods}} +{{/hasAuthMethods}} + +import okhttp3.Call +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Converter +import retrofit2.CallAdapter +import retrofit2.converter.scalars.ScalarsConverterFactory +{{#useRxJava}} +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory +{{/useRxJava}} +{{#useRxJava2}} +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +{{/useRxJava2}} +{{#useRxJava3}} +import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory +{{/useRxJava3}} +{{#gson}} +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import retrofit2.converter.gson.GsonConverterFactory +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Moshi +import retrofit2.converter.moshi.MoshiConverterFactory +{{/moshi}} +{{#jackson}} +import com.fasterxml.jackson.databind.ObjectMapper +import retrofit2.converter.jackson.JacksonConverterFactory +{{/jackson}} + +{{#kotlinx_serialization}} +import retrofit2.converter.kotlinx.serialization.asConverterFactory +import {{packageName}}.infrastructure.Serializer.kotlinxSerializationJson +import okhttp3.MediaType.Companion.toMediaType +{{/kotlinx_serialization}} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class ApiClient( + private var baseUrl: String = defaultBasePath, + private val okHttpClientBuilder: OkHttpClient.Builder? = null, + {{^kotlinx_serialization}} + private val serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = {{#generateOneOfAnyOfWrappers}}{{#gson}}registerTypeAdapterFactoryForAllModels({{/gson}}{{/generateOneOfAnyOfWrappers}}Serializer.{{#gson}}gsonBuilder{{/gson}}{{#generateOneOfAnyOfWrappers}}{{#gson}}){{/gson}}{{/generateOneOfAnyOfWrappers}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}}, + {{/kotlinx_serialization}} + private val callFactory: Call.Factory? = null, + private val callAdapterFactories: List = listOf( + {{#useRxJava}} + RxJavaCallAdapterFactory.create(), + {{/useRxJava}} + {{#useRxJava2}} + RxJava2CallAdapterFactory.create(), + {{/useRxJava2}} + {{#useRxJava3}} + RxJava3CallAdapterFactory.create(), + {{/useRxJava3}} + ), + private val converterFactories: List = listOf( + ScalarsConverterFactory.create(), + {{#gson}} + GsonConverterFactory.create(serializerBuilder.create()), + {{/gson}} + {{#moshi}} + MoshiConverterFactory.create(serializerBuilder.build()), + {{/moshi}} + {{#kotlinx_serialization}} + kotlinxSerializationJson.asConverterFactory("application/json".toMediaType()), + {{/kotlinx_serialization}} + {{#jackson}} + JacksonConverterFactory.create(serializerBuilder), + {{/jackson}} + ) +) { + private val apiAuthorizations = mutableMapOf() + var logger: ((String) -> Unit)? = null + + private val retrofitBuilder: Retrofit.Builder by lazy { + Retrofit.Builder() + .baseUrl(baseUrl) + .apply { + callAdapterFactories.forEach { + addCallAdapterFactory(it) + } + } + .apply { + converterFactories.forEach { + addConverterFactory(it) + } + } + } + + private val clientBuilder: OkHttpClient.Builder by lazy { + okHttpClientBuilder ?: defaultClientBuilder + } + + private val defaultClientBuilder: OkHttpClient.Builder by lazy { + OkHttpClient() + .newBuilder() + .addInterceptor(HttpLoggingInterceptor { message -> logger?.invoke(message) } + .apply { level = HttpLoggingInterceptor.Level.BODY } + ) + } + + init { + normalizeBaseUrl() + } + + {{#hasAuthMethods}} + constructor( + baseUrl: String = defaultBasePath, + okHttpClientBuilder: OkHttpClient.Builder? = null, + {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} + authNames: Array + ) : this(baseUrl, okHttpClientBuilder{{^kotlinx_serialization}}, serializerBuilder{{/kotlinx_serialization}}) { + authNames.forEach { authName -> + val auth: Interceptor? = when (authName) { {{#authMethods}} + {{#isBasicBasic}}"{{name}}" -> HttpBasicAuth() + {{/isBasicBasic}}{{#isBasicBearer}}"{{name}}" -> HttpBearerAuth("{{scheme}}") + {{/isBasicBearer}}{{#isApiKey}}"{{name}}" -> ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}") + {{/isApiKey}}{{#isOAuth}}"{{name}}" -> OAuth(OAuthFlow.{{flow}}, "{{authorizationUrl}}", "{{tokenUrl}}", "{{#scopes}}{{scope}}{{^-last}}, {{/-last}}{{/scopes}}") + {{/isOAuth}}{{^isBasicBasic}}{{^isBasicBearer}}{{^isApiKey}}{{^isOAuth}}"{{name}}" -> null{{/isOAuth}}{{/isApiKey}}{{/isBasicBearer}}{{/isBasicBasic}}{{/authMethods}} + else -> throw RuntimeException("auth name $authName not found in available auth names") + } + if (auth != null) { + addAuthorization(authName, auth) + } + } + {{#generateOneOfAnyOfWrappers}} + {{^kotlinx_serialization}} + {{#gson}} + {{#models}} + {{#model}} + {{^isEnum}} + {{^hasChildren}} + serializerBuilder.registerTypeAdapterFactory({{modelPackage}}.{{{classname}}}.CustomTypeAdapterFactory()) + {{/hasChildren}} + {{/isEnum}} + {{/model}} + {{/models}} + {{/gson}} + {{/kotlinx_serialization}} + {{/generateOneOfAnyOfWrappers}} + } + + {{#authMethods}} + {{#isBasic}} + {{#isBasicBasic}} + constructor( + baseUrl: String = defaultBasePath, + okHttpClientBuilder: OkHttpClient.Builder? = null, + {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} + authName: String, + username: String, + password: String + ) : this(baseUrl, okHttpClientBuilder, {{^kotlinx_serialization}}serializerBuilder, {{/kotlinx_serialization}}arrayOf(authName)) { + setCredentials(username, password) + } + + {{/isBasicBasic}} + {{#isBasicBearer}} + constructor( + baseUrl: String = defaultBasePath, + okHttpClientBuilder: OkHttpClient.Builder? = null, + {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} + authName: String, + bearerToken: String + ) : this(baseUrl, okHttpClientBuilder, {{^kotlinx_serialization}}serializerBuilder, {{/kotlinx_serialization}}arrayOf(authName)) { + setBearerToken(bearerToken) + } + + {{/isBasicBearer}} + {{/isBasic}} + {{/authMethods}} + {{#hasOAuthMethods}} + constructor( + baseUrl: String = defaultBasePath, + okHttpClientBuilder: OkHttpClient.Builder? = null, + {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} + authName: String, + clientId: String, + secret: String, + username: String, + password: String + ) : this(baseUrl, okHttpClientBuilder, {{^kotlinx_serialization}}serializerBuilder, {{/kotlinx_serialization}}arrayOf(authName)) { + getTokenEndPoint() + ?.setClientId(clientId) + ?.setClientSecret(secret) + ?.setUsername(username) + ?.setPassword(password) + } + + {{/hasOAuthMethods}} + {{#authMethods}} + {{#isBasic}} + {{#isBasicBasic}} + fun setCredentials(username: String, password: String): ApiClient { + apiAuthorizations.values.runOnFirst { + setCredentials(username, password) + } + {{#hasOAuthMethods}} + apiAuthorizations.values.runOnFirst { + tokenRequestBuilder.setUsername(username).setPassword(password) + } + {{/hasOAuthMethods}} + return this + } + + {{/isBasicBasic}} + {{^isBasicBasic}} + {{#hasOAuthMethods}} + fun setCredentials(username: String, password: String): ApiClient { + apiAuthorizations.values.runOnFirst { + tokenRequestBuilder.setUsername(username).setPassword(password) + } + return this + } + {{/hasOAuthMethods}} + {{/isBasicBasic}} + {{#isBasicBearer}} + fun setBearerToken(bearerToken: String): ApiClient { + apiAuthorizations.values.runOnFirst { + this.bearerToken = bearerToken + } + return this + } + + {{/isBasicBearer}} + {{/isBasic}} + {{/authMethods}} + {{/hasAuthMethods}} + {{#hasOAuthMethods}} + /** + * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) + * @return Token request builder + */ + fun getTokenEndPoint(): TokenRequestBuilder? { + var result: TokenRequestBuilder? = null + apiAuthorizations.values.runOnFirst { + result = tokenRequestBuilder + } + return result + } + + /** + * Helper method to configure authorization endpoint of the first oauth found in the apiAuthorizations (there should be only one) + * @return Authentication request builder + */ + fun getAuthorizationEndPoint(): AuthenticationRequestBuilder? { + var result: AuthenticationRequestBuilder? = null + apiAuthorizations.values.runOnFirst { + result = authenticationRequestBuilder + } + return result + } + + /** + * Helper method to pre-set the oauth access token of the first oauth found in the apiAuthorizations (there should be only one) + * @param accessToken Access token + * @return ApiClient + */ + fun setAccessToken(accessToken: String): ApiClient { + apiAuthorizations.values.runOnFirst { + setAccessToken(accessToken) + } + return this + } + + /** + * Helper method to configure the oauth accessCode/implicit flow parameters + * @param clientId Client ID + * @param clientSecret Client secret + * @param redirectURI Redirect URI + * @return ApiClient + */ + fun configureAuthorizationFlow(clientId: String, clientSecret: String, redirectURI: String): ApiClient { + apiAuthorizations.values.runOnFirst { + tokenRequestBuilder + .setClientId(clientId) + .setClientSecret(clientSecret) + .setRedirectURI(redirectURI) + authenticationRequestBuilder + ?.setClientId(clientId) + ?.setRedirectURI(redirectURI) + } + return this + } + + /** + * Configures a listener which is notified when a new access token is received. + * @param accessTokenListener Access token listener + * @return ApiClient + */ + fun registerAccessTokenListener(accessTokenListener: AccessTokenListener): ApiClient { + apiAuthorizations.values.runOnFirst { + registerAccessTokenListener(accessTokenListener) + } + return this + } + + {{/hasOAuthMethods}} + /** + * Adds an authorization to be used by the client + * @param authName Authentication name + * @param authorization Authorization interceptor + * @return ApiClient + */ + fun addAuthorization(authName: String, authorization: Interceptor): ApiClient { + if (apiAuthorizations.containsKey(authName)) { + throw RuntimeException("auth name $authName already in api authorizations") + } + apiAuthorizations[authName] = authorization + clientBuilder.addInterceptor(authorization) + return this + } + + fun setLogger(logger: (String) -> Unit): ApiClient { + this.logger = logger + return this + } + + fun createService(serviceClass: Class): S { + val usedCallFactory = this.callFactory ?: clientBuilder.build() + return retrofitBuilder.callFactory(usedCallFactory).build().create(serviceClass) + } + + {{#generateOneOfAnyOfWrappers}} + {{^kotlinx_serialization}} + {{#gson}} + /** + * Gets the serializer builder. + * @return serial builder + */ + fun getSerializerBuilder(): GsonBuilder { + return serializerBuilder + } + + {{/gson}} + {{/kotlinx_serialization}} + {{/generateOneOfAnyOfWrappers}} + private fun normalizeBaseUrl() { + if (!baseUrl.endsWith("/")) { + baseUrl += "/" + } + } + + private inline fun Iterable.runOnFirst(callback: U.() -> Unit) { + for (element in this) { + if (element is U) { + callback.invoke(element) + break + } + } + } + + companion object { + @JvmStatic + protected val baseUrlKey = "{{packageName}}.baseUrl" + + @JvmStatic + val defaultBasePath: String by lazy { + System.getProperties().getProperty(baseUrlKey, "{{{basePath}}}") + } + } +} +{{#generateOneOfAnyOfWrappers}} +{{^kotlinx_serialization}} +{{#gson}} + +/** + * Registers all models with the type adapter factory. + * + * @param gsonBuilder gson builder + * @return GSON builder + */ +fun registerTypeAdapterFactoryForAllModels(gsonBuilder: GsonBuilder): GsonBuilder { + {{#models}} + {{#model}} + {{^isEnum}} + {{^hasChildren}} + gsonBuilder.registerTypeAdapterFactory({{modelPackage}}.{{{classname}}}.CustomTypeAdapterFactory()) + {{/hasChildren}} + {{/isEnum}} + {{/model}} + {{/models}} + return gsonBuilder +} +{{/gson}} +{{/kotlinx_serialization}} +{{/generateOneOfAnyOfWrappers}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache new file mode 100644 index 000000000000..4e8030cb56f3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache @@ -0,0 +1,56 @@ +package {{packageName}}.infrastructure + +class CollectionFormats { + + open class CSVParams { + + var params: List + + constructor(params: List) { + this.params = params + } + + constructor(vararg params: String) { + this.params = listOf(*params) + } + + override fun toString(): String { + return params.joinToString(",") + } + } + + open class SSVParams : CSVParams { + + constructor(params: List) : super(params) + + constructor(vararg params: String) : super(*params) + + override fun toString(): String { + return params.joinToString(" ") + } + } + + class TSVParams : CSVParams { + + constructor(params: List) : super(params) + + constructor(vararg params: String) : super(*params) + + override fun toString(): String { + return params.joinToString("\t") + } + } + + class PIPESParams : CSVParams { + + constructor(params: List) : super(params) + + constructor(vararg params: String) : super(*params) + + override fun toString(): String { + return params.joinToString("|") + } + } + + class SPACEParams : SSVParams() +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache new file mode 100644 index 000000000000..a920e0482022 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache @@ -0,0 +1,35 @@ +package {{packageName}}.infrastructure + +{{#moshi}} +import com.squareup.moshi.JsonDataException +import com.squareup.moshi.Moshi +{{/moshi}} +{{#gson}} +import com.google.gson.GsonBuilder +import com.google.gson.JsonParseException +{{/gson}} +import retrofit2.Response + +{{#moshi}} +@Throws(JsonDataException::class) +inline fun Response<*>.getErrorResponse(serializerBuilder: Moshi.Builder = Serializer.moshiBuilder): T? { + val serializer = serializerBuilder.build() + val parser = serializer.adapter(T::class.java) + val response = errorBody()?.string() + if (response != null) { + return parser.fromJson(response) + } + return null +} +{{/moshi}} +{{#gson}} +@Throws(JsonParseException::class) +inline fun Response<*>.getErrorResponse(serializerBuilder: GsonBuilder = Serializer.gsonBuilder): T? { + val serializer = serializerBuilder.create() + val reader = errorBody()?.charStream() + if (reader != null) { + return serializer.fromJson(reader, T::class.java) + } + return null +} +{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache new file mode 100644 index 000000000000..fe821c41b4df --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache @@ -0,0 +1,5 @@ +{{#allParams}}{{#isDeepObject}} + * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/isDeepObject}}{{^isDeepObject}}{{#isExplode}}{{#hasVars}}{{#vars}} + * @param {{{baseName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/vars}}{{/hasVars}}{{^hasVars}} + * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/hasVars}}{{/isExplode}}{{^isExplode}} + * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/isExplode}}{{/isDeepObject}}{{/allParams}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache new file mode 100644 index 000000000000..685c514e6a90 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache @@ -0,0 +1 @@ +{{#isPathParam}}@Path("{{baseName}}") {{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache new file mode 100644 index 000000000000..86ece566a3c2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache @@ -0,0 +1 @@ +@Query("{{baseName}}") {{{paramName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}@JvmSuppressWildcards {{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{/collectionFormat}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache new file mode 100644 index 000000000000..ab229cf40e47 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache @@ -0,0 +1 @@ +{{#isQueryParam}}{{#isDeepObject}}{{>queryParam}}{{/isDeepObject}}{{^isDeepObject}}{{#isExplode}}{{#hasVars}}{{#vars}}{{>explodedQueryParam}}{{^-last}}, {{/-last}}{{/vars}}{{/hasVars}}{{^hasVars}}{{>queryParam}}{{/hasVars}}{{/isExplode}}{{^isExplode}}{{>queryParam}}{{/isExplode}}{{/isDeepObject}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache new file mode 100644 index 000000000000..1c533aae70a5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache @@ -0,0 +1,147 @@ +{{>licenseInfo}} +package {{apiPackage}} + +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonProperty +{{/jackson}} + +import org.springframework.web.client.RestClient +import org.springframework.web.client.RestClientResponseException + +{{#jackson}} +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter +{{/jackson}} +import org.springframework.http.ResponseEntity +import org.springframework.http.MediaType + + +{{#imports}}import {{import}} +{{/imports}} +import {{packageName}}.infrastructure.* + +{{#operations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(client: RestClient) : ApiClient(client) { + + {{#jackson}} + constructor(baseUrl: String) : this(RestClient.builder() + .baseUrl(baseUrl) + .messageConverters { it.add(MappingJackson2HttpMessageConverter()) } + .build() + ) + {{/jackson}} + + {{#operation}} + {{#allParams}} + {{#isEnum}} + /** + * enum for parameter {{paramName}} + */ + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{^enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/jackson}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{#enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^-last}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/jackson}} + {{/-last}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + } + + {{/isEnum}} + {{/allParams}} + + @Throws(RestClientResponseException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} { + {{#returnType}}val result = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + {{#returnType}} + return result.body!! + {{/returnType}} + } + + @Throws(RestClientResponseException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): ResponseEntity<{{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { + val localVariableConfig = {{operationId}}RequestConfig({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + return request<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}, {{{returnType}}}{{^returnType}}Unit{{/returnType}}>( + localVariableConfig + ) + } + + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}RequestConfig({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : RequestConfig<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}> { + val localVariableBody = {{#hasBodyParam}}{{! + }}{{#bodyParams}}{{{paramName}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{! + }}{{^hasFormParams}}null{{/hasFormParams}}{{! + }}{{#hasFormParams}}mapOf({{#formParams}} + "{{{baseName}}}" to PartConfig(body = {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}, headers = mutableMapOf({{#contentType}}"Content-Type" to "{{contentType}}"{{/contentType}})),{{! + }}{{/formParams}}){{/hasFormParams}}{{! + }}{{/hasBodyParam}} + val localVariableQuery = {{^hasQueryParams}}mutableMapOf>() +{{/hasQueryParams}}{{#hasQueryParams}}mutableMapOf>() + .apply { + {{#queryParams}} + {{^required}} + if ({{{paramName}}} != null) { + put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) + } + {{/required}} + {{#required}} + {{#isNullable}} + if ({{{paramName}}} != null) { + put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) + } + {{/isNullable}} + {{^isNullable}} + put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) + {{/isNullable}} + {{/required}} + {{/queryParams}} + } + {{/hasQueryParams}} + val localVariableHeaders: MutableMap = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}}) + {{#headerParams}} + {{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } + {{/headerParams}} + {{^hasFormParams}}{{#hasConsumes}}{{#consumes}}localVariableHeaders["Content-Type"] = "{{{mediaType}}}" + {{/consumes}}{{/hasConsumes}}{{/hasFormParams}}{{#hasProduces}}localVariableHeaders["Accept"] = "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}" +{{/hasProduces}} + + val params = mutableMapOf( + {{#pathParams}} + "{{baseName}}" to {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}{{/isContainer}}, + {{/pathParams}} + ) + + return RequestConfig( + method = RequestMethod.{{httpMethod}}, + path = "{{path}}", + params = params, + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, + body = localVariableBody + ) + } + + {{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..9d116ed9b175 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,68 @@ +package {{packageName}}.infrastructure; + +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpMethod +import org.springframework.http.MediaType +import org.springframework.web.client.RestClient +import org.springframework.http.ResponseEntity +import org.springframework.util.LinkedMultiValueMap + +open class ApiClient(protected val client: RestClient) { + + protected inline fun request(requestConfig: RequestConfig): ResponseEntity { + return prepare(defaults(requestConfig)) + .retrieve() + .toEntity(object : ParameterizedTypeReference() {}) + } + + protected fun prepare(requestConfig: RequestConfig) = + client.method(requestConfig) + .uri(requestConfig) + .headers(requestConfig) + .nullableBody(requestConfig) + + protected fun defaults(requestConfig: RequestConfig) = + requestConfig.apply { + if (body != null && headers[HttpHeaders.CONTENT_TYPE].isNullOrEmpty()) { + headers[HttpHeaders.CONTENT_TYPE] = MediaType.APPLICATION_JSON_VALUE + } + if (headers[HttpHeaders.ACCEPT].isNullOrEmpty()) { + headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE + } + } + + private fun RestClient.method(requestConfig: RequestConfig)= + method(HttpMethod.valueOf(requestConfig.method.name)) + + private fun RestClient.RequestBodyUriSpec.uri(requestConfig: RequestConfig) = + uri { builder -> + builder + .path(requestConfig.path) + .queryParams(LinkedMultiValueMap(requestConfig.query)) + .build(requestConfig.params) + } + + private fun RestClient.RequestBodySpec.headers(requestConfig: RequestConfig) = + apply { requestConfig.headers.forEach { (name, value) -> header(name, value) } } + + private fun RestClient.RequestBodySpec.nullableBody(requestConfig: RequestConfig) = + apply { if (requestConfig.body != null) body(requestConfig.body) } +} + +inline fun parseDateToQueryString(value : T): String { + {{#toJson}} + /* + .replace("\"", "") converts the json object string to an actual string for the query parameter. + The moshi or gson adapter allows a more generic solution instead of trying to use a native + formatter. It also easily allows to provide a simple way to define a custom date format pattern + inside a gson/moshi adapter. + */ + {{#jackson}} + return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") + {{/jackson}} + {{/toJson}} + {{^toJson}} + return value.toString() + {{/toJson}} + } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache new file mode 100644 index 000000000000..556397463513 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache @@ -0,0 +1,149 @@ +{{>licenseInfo}} +package {{apiPackage}} + +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonProperty +{{/jackson}} + +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.WebClientResponseException +{{#jackson}} +import org.springframework.http.codec.json.Jackson2JsonDecoder +import org.springframework.http.codec.json.Jackson2JsonEncoder +{{/jackson}} +import org.springframework.http.ResponseEntity +import org.springframework.http.MediaType +import reactor.core.publisher.Mono +import org.springframework.util.LinkedMultiValueMap + +{{#imports}}import {{import}} +{{/imports}} +import {{packageName}}.infrastructure.* + +{{#operations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(client: WebClient) : ApiClient(client) { + + {{#jackson}} + constructor(baseUrl: String) : this(WebClient.builder() + .baseUrl(baseUrl) + .codecs { + it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(Serializer.jacksonObjectMapper, MediaType.APPLICATION_JSON)) + it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(Serializer.jacksonObjectMapper, MediaType.APPLICATION_JSON)) + } + .build() + ) + {{/jackson}} + + {{#operation}} + {{#allParams}} + {{#isEnum}} + /** + * enum for parameter {{paramName}} + */ + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{^enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/jackson}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{#enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^-last}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/jackson}} + {{/-last}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + } + + {{/isEnum}} + {{/allParams}} + + @Throws(WebClientResponseException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Mono<{{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { + return {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + .map { {{#returnType}}it.body{{/returnType}}{{^returnType}}Unit{{/returnType}} } + } + + @Throws(WebClientResponseException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Mono> { + val localVariableConfig = {{operationId}}RequestConfig({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + return request<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}, {{{returnType}}}{{^returnType}}Unit{{/returnType}}>( + localVariableConfig + ) + } + + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}RequestConfig({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : RequestConfig<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}> { + val localVariableBody = {{#hasBodyParam}}{{! + }}{{#bodyParams}}{{{paramName}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{! + }}{{^hasFormParams}}null{{/hasFormParams}}{{! + }}{{#hasFormParams}}mapOf({{#formParams}} + "{{{baseName}}}" to PartConfig(body = {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}, headers = mutableMapOf({{#contentType}}"Content-Type" to "{{contentType}}"{{/contentType}})),{{! + }}{{/formParams}}){{/hasFormParams}}{{! + }}{{/hasBodyParam}} + val localVariableQuery = {{^hasQueryParams}}mutableMapOf>() +{{/hasQueryParams}}{{#hasQueryParams}}mutableMapOf>() + .apply { + {{#queryParams}} + {{^required}} + if ({{{paramName}}} != null) { + put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) + } + {{/required}} + {{#required}} + {{#isNullable}} + if ({{{paramName}}} != null) { + put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) + } + {{/isNullable}} + {{^isNullable}} + put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) + {{/isNullable}} + {{/required}} + {{/queryParams}} + } + {{/hasQueryParams}} + val localVariableHeaders: MutableMap = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}}) + {{#headerParams}} + {{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } + {{/headerParams}} + {{^hasFormParams}}{{#hasConsumes}}{{#consumes}}localVariableHeaders["Content-Type"] = "{{{mediaType}}}" + {{/consumes}}{{/hasConsumes}}{{/hasFormParams}}{{#hasProduces}}localVariableHeaders["Accept"] = "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}" +{{/hasProduces}} + + val params = mutableMapOf( + {{#pathParams}} + "{{baseName}}" to {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}{{/isContainer}}, + {{/pathParams}} + ) + + return RequestConfig( + method = RequestMethod.{{httpMethod}}, + path = "{{path}}", + params = params, + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, + body = localVariableBody + ) + } + + {{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..3042e1aecea6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,69 @@ +package {{packageName}}.infrastructure; + +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpMethod +import org.springframework.http.MediaType +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.http.ResponseEntity +import org.springframework.util.LinkedMultiValueMap +import reactor.core.publisher.Mono + +open class ApiClient(protected val client: WebClient) { + + protected inline fun request(requestConfig: RequestConfig): Mono> { + return prepare(defaults(requestConfig)) + .retrieve() + .toEntity(object : ParameterizedTypeReference() {}) + } + + protected fun prepare(requestConfig: RequestConfig) = + client.method(requestConfig) + .uri(requestConfig) + .headers(requestConfig) + .body(requestConfig) + + protected fun defaults(requestConfig: RequestConfig) = + requestConfig.apply { + if (body != null && headers[HttpHeaders.CONTENT_TYPE].isNullOrEmpty()) { + headers[HttpHeaders.CONTENT_TYPE] = MediaType.APPLICATION_JSON_VALUE + } + if (headers[HttpHeaders.ACCEPT].isNullOrEmpty()) { + headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE + } + } + + private fun WebClient.method(requestConfig: RequestConfig)= + method(HttpMethod.valueOf(requestConfig.method.name)) + + private fun WebClient.RequestBodyUriSpec.uri(requestConfig: RequestConfig) = + uri { builder -> + builder + .path(requestConfig.path) + .queryParams(LinkedMultiValueMap(requestConfig.query)) + .build(requestConfig.params) + } + + private fun WebClient.RequestBodySpec.headers(requestConfig: RequestConfig) = + apply { requestConfig.headers.forEach { (name, value) -> header(name, value) } } + + private fun WebClient.RequestBodySpec.body(requestConfig: RequestConfig) = + apply { if (requestConfig.body != null) bodyValue(requestConfig.body) } +} + +inline fun parseDateToQueryString(value : T): String { + {{#toJson}} + /* + .replace("\"", "") converts the json object string to an actual string for the query parameter. + The moshi or gson adapter allows a more generic solution instead of trying to use a native + formatter. It also easily allows to provide a simple way to define a custom date format pattern + inside a gson/moshi adapter. + */ + {{#jackson}} + return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") + {{/jackson}} + {{/toJson}} + {{^toJson}} + return value.toString() + {{/toJson}} + } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache new file mode 100644 index 000000000000..c84f15f0b3fb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache @@ -0,0 +1,232 @@ +{{>licenseInfo}} +package {{apiPackage}} + +import java.io.IOException + +{{#imports}}import {{import}} +{{/imports}} + +{{#jackson}} +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.core.type.TypeReference +{{/jackson}} +{{#gson}} +import com.google.gson.reflect.TypeToken +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +{{/moshi}} + +import io.vertx.core.Vertx +import io.vertx.core.http.RequestOptions +import io.vertx.core.http.HttpMethod +import io.vertx.core.buffer.Buffer +import io.vertx.core.Future +import io.vertx.ext.web.client.WebClient +import io.vertx.uritemplate.UriTemplate + +{{#useCoroutines}} +import io.vertx.kotlin.coroutines.await +import io.vertx.kotlin.coroutines.dispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +{{/useCoroutines}} + +import {{packageName}}.infrastructure.* + +@Suppress ("UNUSED") +{{#operations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(basePath: kotlin.String = ApiClient.defaultBasePath, accessToken: String? = null, apiKey: MutableMap = mutableMapOf(), apiKeyPrefix: MutableMap = mutableMapOf(), username: String? = null, password: String? = null, vertx: Vertx): ApiClient(basePath, accessToken, apiKey, apiKeyPrefix, username, password, vertx) { + {{#operation}} + {{#allParams}} + {{#isEnum}} + /** + * enum for parameter {{paramName}} + */ + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{^enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{#moshi}} + @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/moshi}} + {{#gson}} + @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/kotlinx_serialization}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{#enumUnknownDefaultCase}} + {{#allowableValues}} + {{#enumVars}} + {{^-last}} + {{#moshi}} + @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/moshi}} + {{#gson}} + @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/gson}} + {{#jackson}} + @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/jackson}} + {{#kotlinx_serialization}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), + {{/kotlinx_serialization}} + {{/-last}} + {{/enumVars}} + {{/allowableValues}} + {{/enumUnknownDefaultCase}} + } + + {{/isEnum}} + {{/allParams}} + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{#nullableReturnType}}{{^isResponseOptional}} or null{{/isResponseOptional}}{{/nullableReturnType}}{{#isResponseOptional}} or null{{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}} + * @throws IllegalStateException If the request is not correctly configured + * @throws IOException Rethrows the OkHttp execute method exception + * @throws UnsupportedOperationException If the API returns an informational or redirection response + * @throws ClientException If the API returns a client error response + * @throws ServerException If the API returns a server error response + */{{#returnType}} + @Suppress("UNCHECKED_CAST"){{/returnType}} + @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + {{#useCoroutines}}suspend {{/useCoroutines}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : {{^useCoroutines}}Future<{{/useCoroutines}}{{#returnType}}{{{returnType}}}{{#nullableReturnType}}{{^isResponseOptional}}?{{/isResponseOptional}}{{/nullableReturnType}}{{#isResponseOptional}}?{{/isResponseOptional}}{{/returnType}}{{^returnType}}Unit{{/returnType}}{{^useCoroutines}}>{{/useCoroutines}} { + return {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}).map { localVarResponse -> + when (localVarResponse.responseType) { + ResponseType.Success -> {{#returnType}}(localVarResponse as Success<*>).data as {{{returnType}}}{{#nullableReturnType}}{{^isResponseOptional}}?{{/isResponseOptional}}{{/nullableReturnType}}{{#isResponseOptional}}?{{/isResponseOptional}}{{/returnType}}{{^returnType}}Unit{{/returnType}} + ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.") + ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.") + ResponseType.ClientError -> { + val localVarError = localVarResponse as ClientError<*> + throw ClientException("Client error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) + } + ResponseType.ServerError -> { + val localVarError = localVarResponse as ServerError<*> + throw ServerException("Server error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) + } + } + }{{#useCoroutines}}.await(){{/useCoroutines}} + } + + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}}* @return ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}> + * @throws IllegalStateException If the request is not correctly configured + * @throws IOException Rethrows the OkHttp execute method exception + */{{#returnType}} + @Suppress("UNCHECKED_CAST"){{/returnType}} + @Throws(IllegalStateException::class, IOException::class) + {{#isDeprecated}} + @Deprecated(message = "This operation is deprecated.") + {{/isDeprecated}} + fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : Future> { + val vertxClient = WebClient.create(vertx) + val request = vertxClient.requestAbs(HttpMethod.{{httpMethod}}, UriTemplate.of("$basePath{{path}}"{{#pathParams}}.replace("{"+"{{baseName}}"+"}", encodeURIComponent({{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}.toString(){{/isContainer}})){{/pathParams}})) + + {{#hasFormParams}}request.putHeader("Content-Type", {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}){{/hasFormParams}} + {{#headerParams}}{{{paramName}}}{{^required}}?{{/required}}.apply { request.putHeader("{{baseName}}", {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}})}{{/headerParams}} + {{^hasFormParams}}{{#hasConsumes}} + {{#consumes}} + request.putHeader("Content-Type", "{{{mediaType}}}") + {{/consumes}} + {{/hasConsumes}}{{/hasFormParams}} + {{#hasProduces}}request.putHeader("Accept", "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}"){{/hasProduces}} + + {{#hasFormParams}} + val form = io.vertx.core.MultiMap.caseInsensitiveMultiMap(); + {{#formParams}} + {{{paramName}}}{{^required}}?{{/required}}.let { form.add("{{{baseName}}}", {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}{{^isString}}.toString(){{/isString}}) } + {{/formParams}} + {{/hasFormParams}} + + {{#hasQueryParams}} + {{#queryParams}} + {{{paramName}}}{{^required}}?{{/required}}.let { request.queryParams().add("{{baseName}}", {{#isContainer}}toMultiValue(it.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString(it){{/isDateTime}}{{#isDate}}parseDateToQueryString(it){{/isDate}}{{^isDateTime}}{{^isDate}}it.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) } + {{/queryParams}} + {{/hasQueryParams}} + + {{#authMethods}} + {{#isApiKey}} + if (apiKey["{{keyParamName}}"] != null) { + if (apiKeyPrefix["{{keyParamName}}"] != null) { + {{#isKeyInHeader}} + request.putHeader("{{keyParamName}}", apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!) + {{/isKeyInHeader}} + {{#isKeyInQuery}} + request.queryParams().add("{{keyParamName}}", apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!) + {{/isKeyInQuery}} + } else { + {{#isKeyInHeader}} + request.putHeader("{{keyParamName}}", apiKey["{{keyParamName}}"]!!) + {{/isKeyInHeader}} + {{#isKeyInQuery}} + request.queryParams().add("{{keyParamName}}", apiKey["{{keyParamName}}"]!!) + {{/isKeyInQuery}} + } + } + {{/isApiKey}} + {{#isBasic}} + {{#isBasicBasic}} + username?.let { username -> + password?.let { password -> + request.basicAuthentication(username, password) + } + } + {{/isBasicBasic}} + {{#isBasicBearer}} + accessToken?.let { accessToken -> + request.bearerTokenAuthentication(accessToken) + } + {{/isBasicBearer}} + {{/isBasic}} + {{#isOAuth}} + accessToken?.let { accessToken -> + request.bearerTokenAuthentication(accessToken) + } + {{/isOAuth}} + {{/authMethods}} + + return request + {{#hasBodyParam}} + .sendBuffer(responseBody({{#bodyParams}}{{{paramName}}}{{/bodyParams}})) + {{/hasBodyParam}} + {{^hasBodyParam}} + .send() + {{/hasBodyParam}} + .map { + val apiResponse: ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}> = handleResponse(it) + apiResponse + } + } + + {{/operation}} + + private inline fun responseBody(body: T): Buffer { + {{#moshi}} + return Buffer.buffer(Serializer.moshi.adapter(T::class.java).toJson(body)) + {{/moshi}} + {{#gson}} + return Buffer.buffer(Serializer.gson.toJson(body, T::class.java)) + {{/gson}} + {{#jackson}} + return Buffer.buffer(Serializer.jacksonObjectMapper.writeValueAsBytes(body)) + {{/jackson}} + } + +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..dacce6f4742a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,110 @@ +package {{packageName}}.infrastructure + +import io.vertx.core.Vertx +import io.vertx.core.buffer.Buffer +import java.nio.charset.StandardCharsets +{{#jackson}} +import com.fasterxml.jackson.core.type.TypeReference +{{/jackson}} +{{#gson}} +import com.google.gson.reflect.TypeToken +{{/gson}} + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(val basePath: kotlin.String = defaultBasePath, val accessToken: String? = null, val apiKey: MutableMap = mutableMapOf(), val apiKeyPrefix: MutableMap = mutableMapOf(), var username: String? = null, var password: String? = null, val vertx: Vertx) { + companion object { + const val baseUrlKey = "{{packageName}}.baseUrl" + + @JvmStatic + val defaultBasePath: String by lazy { + System.getProperties().getProperty(baseUrlKey, "{{{basePath}}}") + } + } + + protected inline fun handleResponse(response: io.vertx.ext.web.client.HttpResponse): ApiResponse { + val code = response.statusCode() + val headers = response.headers().associate { it.key to listOf(it.value) } + val contentType = headers["Content-Type"]?.firstOrNull()?.substringBefore(";")?.lowercase(java.util.Locale.getDefault()) + + return when (code) { + in 100..199 -> Informational( + response.statusMessage(), + code, + headers + ) + in 200 .. 299 -> Success( + responseBody(response.body(), contentType), + code, + headers + ) + in 300..399 -> Redirection( + code, + headers + ) + in 400..499 -> ClientError( + response.statusMessage(), + response.bodyAsString(), + code, + headers + ) + else -> ServerError( + response.statusMessage(), + response.bodyAsString(), + code, + headers + ) + } + } + + protected inline fun responseBody(body: Buffer?, mediaType: String? = "application/json"): T? { + body ?: return null + + val bodyContent = String(body.bytes, StandardCharsets.UTF_8) + if (bodyContent.isEmpty()) { + return null + } + + return when { + mediaType==null || (mediaType.startsWith("application/") && mediaType.endsWith("json")) -> + {{#moshi}}Serializer.moshi.adapter(T::class.java).fromJson(bodyContent){{/moshi}}{{! + }}{{#gson}}Serializer.gson.fromJson(bodyContent, (object: TypeToken(){}).getType()){{/gson}}{{! + }}{{#jackson}}Serializer.jacksonObjectMapper.readValue(bodyContent, object: TypeReference() {}){{/jackson}}{{! + }}{{#kotlinx_serialization}}Serializer.kotlinxSerializationJson.decodeFromString(bodyContent){{/kotlinx_serialization}} + else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.") + } + } + + protected fun encodeURIComponent(parameter: String): String { + return try { + java.net.URLEncoder.encode(parameter, java.nio.charset.StandardCharsets.UTF_8.name()) + } catch (e: java.io.UnsupportedEncodingException) { + parameter + } + } + + protected inline fun parseDateToQueryString(value : T): String { + {{#toJson}} + /* + .replace("\"", "") converts the json object string to an actual string for the query parameter. + The moshi or gson adapter allows a more generic solution instead of trying to use a native + formatter. It also easily allows to provide a simple way to define a custom date format pattern + inside a gson/moshi adapter. + */ + {{#moshi}} + return Serializer.moshi.adapter(T::class.java).toJson(value).replace("\"", "") + {{/moshi}} + {{#gson}} + return Serializer.gson.toJson(value, T::class.java).replace("\"", "") + {{/gson}} + {{#jackson}} + return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") + {{/jackson}} + {{#kotlinx_serialization}} + return Serializer.kotlinxSerializationJson.encodeToString(value).replace("\"", "") + {{/kotlinx_serialization}} + {{/toJson}} + {{^toJson}} + return value.toString() + {{/toJson}} + } + +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache new file mode 100644 index 000000000000..d529ad5599f1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache @@ -0,0 +1,43 @@ +package {{packageName}}.infrastructure + +{{#nonPublicApi}}internal {{/nonPublicApi}}enum class ResponseType { + Success, Informational, Redirection, ClientError, ServerError +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}interface Response + +{{#nonPublicApi}}internal {{/nonPublicApi}}abstract class ApiResponse(val responseType: ResponseType): Response { + abstract val statusCode: Int + abstract val headers: Map> +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class Success( + val data: T{{#nullableReturnType}}?{{/nullableReturnType}}, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +): ApiResponse(ResponseType.Success) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class Informational( + val statusText: String, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiResponse(ResponseType.Informational) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class Redirection( + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiResponse(ResponseType.Redirection) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class ClientError( + val message: String? = null, + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiResponse(ResponseType.ClientError) + +{{#nonPublicApi}}internal {{/nonPublicApi}}class ServerError( + val message: String? = null, + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> +): ApiResponse(ResponseType.ServerError) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache new file mode 100644 index 000000000000..1357da8d599c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache @@ -0,0 +1,18 @@ +@file:Suppress("unused") +package {{packageName}}.infrastructure + +import java.lang.RuntimeException + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ClientException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private const val serialVersionUID: Long = 123L + } +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ServerException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private const val serialVersionUID: Long = 456L + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache new file mode 100644 index 000000000000..4aff1e8f1bc6 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache @@ -0,0 +1,230 @@ +# {{packageName}} - Kotlin client library for {{appName}} + + +A kotlin client for Android using the currently recommended http client, Volley. See https://developer.android.com/training/volley + +- Currently sends GsonRequests +- Currently only supports Gson as a serializer - will throw an exception if a different serializer is chosen +- Defaults the source location to src/main/java as per standard Android builds + + +## Design + +Volley is a queue/request based layer on top of http url stack specific to Android. Android favours dependency injection and +a layered architecture, and IO performed off the main thread to maintain UI responsiveness, with a preferred technique of +kotlin co-routines. The code gen library reflects these factors. + +- Api calls use co-routines, and execute them using volley callbacks to avoid tying up a thread. +- Facilitate dependency injection, with default implementations available. +- Generate a requestFactory that can be overridden +- Allow the passing of the RequestFactory per tag (api client) or per operation (an extra parameter is created on operations with non-global security), with per operation auth overriding global security. +- DI scoping of the Request Factory and pre-generated auth header factories allow for thread safe and secure setting of credentials. +- Lazy header factories allow for refreshing tokens etc +- Factoring of header factories to the Request Factory allow ambient provision of credentials. Code gen library is credential storage agnostic. +- Header factories allow the merging of generated headers from open api spec with dynamically added headers + +- Injection of http url stack to allow custom http stacks. Default implementation is best practice singleton +- Data classes used for serialisation to reflect volley's preference - an immutable request that once queued can't be tampered with. + +- Reuse model class and other jvm common infrastructure + +- Optional generation of room database models, and transform methods to these from open api models +- Room and api models can be extended with additional extension properties. + +## Future improvements +- Option to generate image requests on certain conditionals e.g content-type gif etc +- Support for kotlin serialization. +- Multi part form parameters and support for file inputs + +## Usage +Hilt Dependency injection example - with default values for parameters overridden. +``` + @Provides + internal fun provideSomeApi( + context: Context, + restService: IRestService, + configurationService: IConfigurationService, + sessionService: ISessionService + ): SomeApi { + return SomeApi( + context = context, + requestQueue = restService.getRequestQueue(), + requestFactory = RequestFactory(listOf(createSessionHeaderFactory(sessionService), createTraceHeaderFactory()), + postProcessors = listOf(retryPolicySetter)), + basePath = configurationService.getBaseUrl() + ) + } +``` +Here is the constructor so you can see the defaults +```class SomeApi ( +val context: Context, +val requestQueue: Lazy = lazy(initializer = { + Volley.newRequestQueue(context.applicationContext) + }), + val requestFactory: IRequestFactory = RequestFactory(), + val basePath: String = "https://yourbasepath.from_input_parameter.com/api", + private val postProcessors :List <(Request<*>) -> Unit> = listOf()) { +``` + +### Overriding defaults +The above constructor for each api allows the following to be customized +- A custom context, so either a singleton request queue or different scope can be created - see +https://developer.android.com/training/volley/requestqueue#singleton +- An overridable request queue - which in turn can have a custom http url stack passed to it +- An overridable request factory constructor call, or a request factory that can be overridden by a custom template, with +custom header factory, request post processors and custom gson adapters injected. + +#### Overriding request generation +Request generation can be overridden by +- Overriding the entire request factory template +- Supplying custom header factories - methods that take any possible parameters but return a map of headers +- Supplying custom request post processors - methods that take and return the request object + +Header factory examples can be found in the auth section, as these are implemented as header factories. eg +``` +val basicAuthHeaderFactoryBuilder = { username: String?, password: String? -> +{ mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))} +} +``` +In this case it's a lambda function (a factory method) that takes an username and password, and returns a map of headers. Other +generated code will supply the username and password. In this case it results in a map of just one key/value pair, but +it could be multiple. The important part is it's returning a map - and that the surrounding code +will can bind the inputs to it at some point. + +Here is a different example that supplies tracing header values +``` +/** + * Create a lambda of tracing headers to be injected into an API's [RequestFactory]. + */ +private fun createTraceHeaderFactory(): () -> Map = { + mapOf( + HttpHeaderType.b3_traceId.rawValue to UUIDExtensions.asTraceId(UUID.randomUUID()), + HttpHeaderType.b3_spanId.rawValue to UUIDExtensions.asSpanId(UUID.randomUUID()), + HttpHeaderType.b3_sampled.rawValue to "1" + ) +} +``` +Finally a post processor example +``` + /** + * Configure a [DefaultRetryPolicy] to be injected into the [RequestFactory] with a maximum number of retries of zero. + */ + private val retryPolicySetter = { request: Request<*> -> + Unit.apply { + request.setRetryPolicy( + DefaultRetryPolicy( + RestService.DEFAULT_TIMEOUT_MS, + 0, + DefaultRetryPolicy.DEFAULT_BACKOFF_MULT + ) + ) + } + } +``` + +### Serialization +#### Gson and Polymorphic types +The GsonRequest object can be passed custom type adapters +``` +class GsonRequest( + method: Int, + url: String, + private val body: Any?, + private val headers: Map?, + private val params: MutableMap?, + private val contentTypeForBody: String?, + private val encodingForParams: String?, + private val gsonAdapters: Map?, + private val type: Type, + private val listener: Response.Listener, + errorListener: Response.ErrorListener +) : Request(method, url, errorListener) { + + val gsonBuilder: GsonBuilder = GsonBuilder() + .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) + .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter()) + .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) + .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) + +``` +## Requires + +{{#jvm}} +* Kotlin 1.4.30 +* Gradle 6.8.3 +{{/jvm}} +{{#multiplatform}} +* Kotlin 1.5.10 +{{/multiplatform}} + +## Build + +{{#jvm}} +First, create the gradle wrapper script: + +``` +gradle wrapper +``` + +Then, run: + +{{/jvm}} +``` +./gradlew check assemble +``` + +This runs all tests and packages the library. + +{{#generateApiDocs}} + +## Documentation for API Endpoints + +All URIs are relative to *{{{basePath}}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +{{/generateApiDocs}} + +{{#generateModelDocs}} + +## Documentation for Models + +{{#modelPackage}} +{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) +{{/model}}{{/models}} +{{/modelPackage}} +{{^modelPackage}} +No model defined in this package +{{/modelPackage}} +{{/generateModelDocs}} + + +## Documentation for Authorization + +{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} +{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasicBasic}}- **Type**: HTTP basic authentication +{{/isBasicBasic}} +{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} +{{/isBasicBearer}} +{{#isHttpSignature}}- **Type**: HTTP signature authentication +{{/isHttpSignature}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache new file mode 100644 index 000000000000..515c3c6efbaf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache @@ -0,0 +1,116 @@ +package {{apiPackage}} + +import android.content.Context +import com.android.volley.DefaultRetryPolicy +import com.android.volley.Request +import com.android.volley.RequestQueue +import com.android.volley.Response +import com.android.volley.toolbox.BaseHttpStack +import com.android.volley.toolbox.Volley +import java.util.* +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import com.google.gson.reflect.TypeToken + +import {{packageName}}.request.IRequestFactory +import {{packageName}}.request.RequestFactory +import {{packageName}}.infrastructure.CollectionFormats.* + +{{#imports}}import {{import}} +{{/imports}} + +{{#operations}} +/* +* If you wish to use a custom http stack with your client you +* can pass that to the request queue like: +* Volley.newRequestQueue(context.applicationContext, myCustomHttpStack) +*/ +class {{classname}} ( + private val context: Context, + private val requestQueue: Lazy = lazy(initializer = { + Volley.newRequestQueue(context.applicationContext) + }), + private val requestFactory: IRequestFactory = RequestFactory(), + private val basePath: String = "{{{basePath}}}", + private val postProcessors :List <(Request<*>) -> Unit> = listOf()) { + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} + */ + {{#isDeprecated}} + @Deprecated("This api was deprecated") + {{/isDeprecated}} + suspend fun {{operationId}}({{^allParams}}){{/allParams}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{#-last}}{{#operationAuthMethod}}, opAuthHeaderFactory = () -> map{{/operationAuthMethod}}){{/-last}}{{/allParams}}: {{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit{{/returnType}} { + {{#bodyParam}} + val body: Any? = {{paramName}} + {{/bodyParam}} + {{^bodyParam}} + val body: Any? = null + {{/bodyParam}} + + val contentTypes : Array = arrayOf({{#consumes}}"{{{mediaType}}}"{{^-last}},{{/-last}}{{/consumes}}) + val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" } + + // Do some work or avoid some work based on what we know about the model, + // before we delegate to a pluggable request factory template + // The request factory template contains only pure code and no templates + // to make it easy to override with your own. + + // create path and map variables + val path = "{{{path}}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}IRequestFactory.escapeString({{{paramName}}}.toString()){{/isContainer}}){{/pathParams}} + + val formParams = mapOf({{^formParams}}){{/formParams}}{{#formParams}} + "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}){{^-last}},{{/-last}}{{#-last}} + ){{/-last}}{{/formParams}} + + + // TODO: Cater for allowing empty values + // TODO, if its apikey auth, then add the header names here and the hardcoded auth key + // Only support hard coded apikey in query param auth for when we do this first path + val queryParams = mapOf({{^queryParams}}){{/queryParams}}{{#queryParams}} + "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}){{^-last}},{{/-last}}{{#-last}} + ){{/-last}}{{/queryParams}} + .filter { it.value.isNotEmpty() } + + val headerParams: Map = mapOf({{^headerParams}}){{/headerParams}}{{#headerParams}} + "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}){{^-last}},{{/-last}}{{#-last}} + ){{/-last}}{{/headerParams}} + + return suspendCoroutine { continuation -> + val responseListener = Response.Listener<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { response -> + continuation.resume(response) + } + + val errorListener = Response.ErrorListener { error -> + continuation.resumeWithException(error) + } + + val responseType = object : TypeToken<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}>() {}.type + + // Call the correct request builder based on whether we have a return type or a body. + // All other switching on types must be done in code inside the builder + val request: Request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> = requestFactory.build( + Request.Method.{{httpMethod}}, + "$basePath$path", + body, + headerParams, + queryParams, + formParams, + contentType, + responseType, + responseListener, + errorListener) + + postProcessors.forEach { it.invoke(request) } + + requestQueue.value.add(request) + } + } + {{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache new file mode 100644 index 000000000000..f7bf73ae31d0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache @@ -0,0 +1,83 @@ +# {{classname}}{{#description}} +{{description}}{{/description}} + +All URIs are relative to *{{basePath}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +{{summary}}{{#notes}} + +{{notes}}{{/notes}} + +### Example +```kotlin +// Import classes: +//import {{{packageName}}}.* +//import {{{packageName}}}.infrastructure.* +//import {{{modelPackage}}}.* + +val apiClient = ApiClient() +{{#authMethods}} +{{#isBasic}} +{{#isBasicBasic}} +apiClient.setCredentials("USERNAME", "PASSWORD") +{{/isBasicBasic}} +{{#isBasicBearer}} +apiClient.setBearerToken("TOKEN") +{{/isBasicBearer}} +{{/isBasic}} +{{/authMethods}} +val webService = apiClient.createWebservice({{{classname}}}::class.java) +{{#allParams}} +val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} +{{/allParams}} + +{{#useCoroutines}} +launch(Dispatchers.IO) { +{{/useCoroutines}} +{{#useCoroutines}} {{/useCoroutines}}{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}webService.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) +{{#useCoroutines}} +} +{{/useCoroutines}} +``` + +### Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} +{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{defaultValue}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} +{{/allParams}} + +### Return type + +{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}} +{{#authMethods}} +{{#isBasic}} +{{#isBasicBasic}} +Configure {{name}}: + ApiClient().setCredentials("USERNAME", "PASSWORD") +{{/isBasicBasic}} +{{#isBasicBearer}} +Configure {{name}}: + ApiClient().setBearerToken("TOKEN") +{{/isBasicBearer}} +{{/isBasic}} +{{/authMethods}} + +### HTTP request headers + + - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} + - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache new file mode 100644 index 000000000000..919da64ce3de --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache @@ -0,0 +1,16 @@ + // Api Key auth supports query header and cookie. + // Query is supported in the path generation only with a hardcoded value. + // TODO: Not sure about cookie auth form + // If implementing api key in query parameter use the ^isKeyInHeader property + + val apiKeyAuthHeaderFactoryBuilder = { + paramName: String, apiKeyPrefix: String?, apiKey: String? -> { + mapOf(paramName to + if (apiKeyPrefix != null) { + "$apiKeyPrefix $apiKey" + } else { + apiKey!! + } + ) + } + } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache new file mode 100644 index 000000000000..5c3fb1c77c26 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache @@ -0,0 +1,15 @@ +companion object Authentication { + // Where a header factory requires parameters a client will need to bind these + // TODO Generate appropriate header factories based on settings + {{#authMethods}} +{{#isApiKey}} +{{>auth/apikeyauth}} +{{/isApiKey}} +{{#isBasicBasic}} +{{>auth/httpbasicauth}} +{{/isBasicBasic}} +{{#isOAuth}} + // TODO: Oauth not implemented yet - comment out below as OAuth does not exist +{{/isOAuth}} + {{/authMethods}} + } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache new file mode 100644 index 000000000000..f4cf1e11fe6b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache @@ -0,0 +1,3 @@ +val basicAuthHeaderFactoryBuilder = { username: String?, password: String? -> + { mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))} +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache new file mode 100644 index 000000000000..81a285bc4782 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache @@ -0,0 +1,5 @@ +val basicAuthHeaderFactoryBuilder = { username: String?, password: String? -> +{ + throw NotImplementedError("OAuth security scheme header factory not implemented yet - see open api generator auth/oauth.mustache") + mapOf("" to "")} +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache new file mode 100644 index 000000000000..886aecf65e30 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache @@ -0,0 +1 @@ +{{#isBodyParam}}{{{paramName}}}: {{{dataType}}}{{^required}}? = null{{/required}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache new file mode 100644 index 000000000000..62f0d98e6e35 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache @@ -0,0 +1,111 @@ +{{#useAndroidMavenGradlePlugin}} +group = '{{groupId}}' +project.version = '{{artifactVersion}}' +{{/useAndroidMavenGradlePlugin}} + +buildscript { + + ext.kotlin_version = '1.5.20' + ext.swagger_annotations_version = "1.6.2" + ext.gson_version = "2.8.6" + ext.volley_version = "1.2.0" + ext.junit_version = "4.13.2" + ext.robolectric_version = "4.5.1" + ext.concurrent_unit_version = "0.4.6" + + repositories { + mavenLocal() + google() + maven { + url 'https://dl.google.com/dl/android/maven2' + } + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.android.tools.build:gradle:7.4.2' + {{#useAndroidMavenGradlePlugin}} + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + {{/useAndroidMavenGradlePlugin}} + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +{{#useAndroidMavenGradlePlugin}} +apply plugin: 'com.github.dcendents.android-maven' +{{/useAndroidMavenGradlePlugin}} + +android { + compileSdkVersion 30 + defaultConfig { + minSdkVersion 21 + targetSdkVersion 30 + } + compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + lintOptions { + abortOnError false + } + + // Rename the aar correctly + libraryVariants.all { variant -> + variant.outputs.all { output -> + if (outputFile != null && outputFileName.endsWith('.aar')) { + outputFileName = "${archivesBaseName}-${version}.aar" + } + } + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "io.swagger:swagger-annotations:$swagger_annotations_version" + implementation "com.google.code.gson:gson:$gson_version" + implementation "com.android.volley:volley:${volley_version}" + testImplementation "junit:junit:$junit_version" + testImplementation "org.robolectric:robolectric:${robolectric_version}" + testImplementation "net.jodah:concurrentunit:${concurrent_unit_version}" + {{#generateRoomModels}} + annotationProcessor "androidx.room:room-runtime:2.3.0" + implementation "androidx.room:room-runtime:2.3.0" + {{/generateRoomModels}} +} + +afterEvaluate { + android.libraryVariants.all { variant -> + def task = project.tasks.create "jar${variant.name.capitalize()}", Jar + task.description = "Create jar artifact for ${variant.name}" + task.dependsOn variant.javaCompile + task.from variant.javaCompile.destinationDirectory + task.destinationDirectory = project.file("${project.buildDir}/outputs/jar") + task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar" + artifacts.add('archives', task); + } +} + +{{#useAndroidMavenGradlePlugin}} +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +artifacts { + archives sourcesJar +} +{{/useAndroidMavenGradlePlugin}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache new file mode 100644 index 000000000000..98427cd4817c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache @@ -0,0 +1 @@ +{{#isFormParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache new file mode 100644 index 000000000000..5b018d630a7d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache @@ -0,0 +1,4 @@ +android.useAndroidX=true +{{#generateRoomModels}} +android.enableJetifier=true +{{/generateRoomModels}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache new file mode 100644 index 000000000000..3e5c64b2cd8e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache @@ -0,0 +1 @@ +{{#isHeaderParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache new file mode 100644 index 000000000000..4e8030cb56f3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache @@ -0,0 +1,56 @@ +package {{packageName}}.infrastructure + +class CollectionFormats { + + open class CSVParams { + + var params: List + + constructor(params: List) { + this.params = params + } + + constructor(vararg params: String) { + this.params = listOf(*params) + } + + override fun toString(): String { + return params.joinToString(",") + } + } + + open class SSVParams : CSVParams { + + constructor(params: List) : super(params) + + constructor(vararg params: String) : super(*params) + + override fun toString(): String { + return params.joinToString(" ") + } + } + + class TSVParams : CSVParams { + + constructor(params: List) : super(params) + + constructor(vararg params: String) : super(*params) + + override fun toString(): String { + return params.joinToString("\t") + } + } + + class PIPESParams : CSVParams { + + constructor(params: List) : super(params) + + constructor(vararg params: String) : super(*params) + + override fun toString(): String { + return params.joinToString("|") + } + } + + class SPACEParams : SSVParams() +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache new file mode 100644 index 000000000000..48bb25d19b56 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache @@ -0,0 +1,9 @@ +{{>licenseInfo}} +package {{packageName}}.infrastructure + +import {{roomModelPackage}}.* + +// TODO ITransformForStorage +interface ITransformForStorage { + fun toRoomModel(): T +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache new file mode 100644 index 000000000000..1ea918bad732 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache new file mode 100644 index 000000000000..4cc7de2240bd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache @@ -0,0 +1 @@ +{{#isPathParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache new file mode 100644 index 000000000000..da0a0c0930ac --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache @@ -0,0 +1 @@ +{{#isQueryParam}}{{{paramName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}{{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{{dataType}}}{{/collectionFormat}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache new file mode 100644 index 000000000000..9bda9b1f0b62 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache @@ -0,0 +1,119 @@ +package {{packageName}}.request + +import com.android.volley.NetworkResponse +import com.android.volley.ParseError +import com.android.volley.Request +import com.android.volley.Response +import com.android.volley.toolbox.HttpHeaderParser +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonSyntaxException +import java.io.UnsupportedEncodingException +import java.nio.charset.Charset +import java.net.HttpURLConnection +import java.lang.reflect.Type +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.OffsetDateTime + +import {{packageName}}.infrastructure.OffsetDateTimeAdapter +import {{packageName}}.infrastructure.LocalDateTimeAdapter +import {{packageName}}.infrastructure.LocalDateAdapter +import {{packageName}}.infrastructure.ByteArrayAdapter + +class GsonRequest( + method: Int, + url: String, + private val body: Any?, + private val headers: Map?, + private val params: MutableMap?, + private val contentTypeForBody: String?, + private val encodingForParams: String?, + private val gsonAdapters: Map?, + private val type: Type, + private val listener: Response.Listener, + errorListener: Response.ErrorListener +) : Request(method, url, errorListener) { + + val gsonBuilder: GsonBuilder = GsonBuilder() + .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) + .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter()) + .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) + .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) + .apply { + gsonAdapters?.forEach { + this.registerTypeAdapter(it.key, it.value) + } + } + + val gson: Gson by lazy { + gsonBuilder.create() + } + + private var response: NetworkResponse? = null + + override fun deliverResponse(response: T?) { + listener.onResponse(response) + } + + override fun getParams(): MutableMap? = params ?: super.getParams() + + override fun getBodyContentType(): String = contentTypeForBody ?: super.getBodyContentType() + + override fun getParamsEncoding(): String = encodingForParams ?: super.getParamsEncoding() + + override fun getHeaders(): MutableMap { + val combined = HashMap() + combined.putAll(super.getHeaders()) + if (headers != null) { + combined.putAll(headers) + } + return combined + } + + override fun getBody(): ByteArray? { + if (body != null) { + return gson.toJson(body).toByteArray(Charsets.UTF_8) + } + return super.getBody() + } + + override fun parseNetworkResponse(response: NetworkResponse?): Response { + return try { + this.response = copyTo(response) + val json = String( + response?.data ?: ByteArray(0), + Charset.forName(HttpHeaderParser.parseCharset(response?.headers)) + ) + Response.success( + gson.fromJson(json, type), + HttpHeaderParser.parseCacheHeaders(response) + ) + } catch (e: UnsupportedEncodingException) { + Response.error(ParseError(e)) + } catch (e: JsonSyntaxException) { + Response.error(ParseError(e)) + } + } + + private fun copyTo(response: NetworkResponse?): NetworkResponse { + return if (response != null) { + NetworkResponse( + response.statusCode, + response.data, + response.notModified, + response.networkTimeMs, + response.allHeaders + ) + } else { + // Return an empty response. + NetworkResponse( + HttpURLConnection.HTTP_BAD_METHOD, + ByteArray(0), + false, + 0, + emptyList() + ) + } + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache new file mode 100644 index 000000000000..6a007c4194d9 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache @@ -0,0 +1,64 @@ +package {{packageName}}.request + +import com.android.volley.Request +import com.android.volley.Response +import java.io.UnsupportedEncodingException +import java.lang.reflect.Type +import java.net.URLEncoder +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* +import java.time.format.DateTimeFormatter +import java.time.OffsetDateTime +import java.time.LocalDate + + +interface IRequestFactory { + + companion object { + /** + * ISO 8601 date time format. + * @see https://en.wikipedia.org/wiki/ISO_8601 + */ + fun formatDateTime(datetime: OffsetDateTime) = DateTimeFormatter.ISO_INSTANT.format(datetime) + fun formatDate(date: LocalDate) = DateTimeFormatter.ISO_LOCAL_DATE.format(date) + + fun escapeString(str: String): String { + return try { + URLEncoder.encode(str, "UTF-8") + } catch (e: UnsupportedEncodingException) { + str + } + } + + fun parameterToString(param: Any?) = + when (param) { + null -> "" + is OffsetDateTime -> formatDateTime(param) + is Collection<*> -> { + val b = StringBuilder() + for (o in param) { + if (b.isNotEmpty()) { + b.append(",") + } + b.append(o.toString()) + } + b.toString() + } + else -> param.toString() + } + } + + + fun build( + method: Int, + url : String, + body: Any?, + headers: Map?, + queryParams: Map?, + formParams: Map?, + contentTypeForBody: String?, + type: Type, + responseListener: Response.Listener, + errorListener: Response.ErrorListener): Request +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache new file mode 100644 index 000000000000..46507bff1b5d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache @@ -0,0 +1,69 @@ +// Knowing the details of an operation it will produce a call to a Volley Request constructor +package {{packageName}}.request + + +import com.android.volley.Request +import com.android.volley.Response +{{#hasAuthMethods}} +import android.util.Base64 +{{/hasAuthMethods}} +import {{packageName}}.request.IRequestFactory.Companion.escapeString +import java.lang.reflect.Type +import java.util.Locale +import java.util.UUID + +class RequestFactory(private val headerFactories : List<() -> Map> = listOf(), private val postProcessors :List <(Request<*>) -> Unit> = listOf(), private val gsonAdapters: Map = mapOf()): IRequestFactory { +{{#hasAuthMethods}} + + {{>auth/authentication}} +{{/hasAuthMethods}} + + /** + * {@inheritDoc} + */ + @Suppress("UNCHECKED_CAST") + override fun build( + method: Int, + url: String, + body: Any?, + headers: Map?, + queryParams: Map?, + formParams: Map?, + contentTypeForBody: String?, + type: Type, + responseListener: Response.Listener, + errorListener: Response.ErrorListener + ): Request { + val afterMarketHeaders = (headers?.toMutableMap() ?: mutableMapOf()) + // Factory built and aftermarket + // Merge the after market headers on top of the base ones in case we are overriding per call auth + val allHeaders = headerFactories.fold(afterMarketHeaders) { acc, factory -> (acc + factory.invoke()).toMutableMap() } + + // If we decide to support auth parameters in the url, then you will reference them by supplying a url string + // with known variable name references in the string. We will then apply + val updatedUrl = if (!queryParams.isNullOrEmpty()) { + queryParams.asSequence().fold("$url?") {acc, param -> + "$acc${escapeString(param.key)}=${escapeString(param.value)}&" + }.trimEnd('&') + } else { + url + } + + val request = GsonRequest( + method, + updatedUrl, + body, + allHeaders, + formParams?.toMutableMap(), + contentTypeForBody, + null, + gsonAdapters, + type, + responseListener, + errorListener) + + postProcessors.forEach{ it.invoke(request)} + + return request + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache new file mode 100644 index 000000000000..249f8edbb632 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache @@ -0,0 +1,135 @@ +{{>licenseInfo}} +package {{apiPackage}} + +{{#imports}}import {{import}} +{{/imports}} + +import {{packageName}}.infrastructure.* +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.request.forms.formData +import io.ktor.client.engine.HttpClientEngine +import kotlinx.serialization.json.Json +import io.ktor.http.ParametersBuilder +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +{{#operations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}open class {{classname}} : ApiClient { + + constructor( + baseUrl: String = ApiClient.BASE_URL, + httpClientEngine: HttpClientEngine? = null, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonSerializer: Json = ApiClient.JSON_DEFAULT + ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) + + constructor( + baseUrl: String, + httpClient: HttpClient + ): super(baseUrl = baseUrl, httpClient = httpClient) + + {{#operation}} + {{#allParams}} + {{#isEnum}} + + /** + * enum for parameter {{paramName}} + */ + @Serializable + {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { + {{^enumUnknownDefaultCase}} + {{#allowableValues}}{{#enumVars}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) + {{&name}}({{{value}}}){{^-last}},{{/-last}} + {{/enumVars}}{{/allowableValues}} + {{/enumUnknownDefaultCase}} + {{#enumUnknownDefaultCase}} + {{#allowableValues}}{{#enumVars}}{{^-last}} + @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) + {{&name}}({{{value}}}), + {{/-last}}{{/enumVars}}{{/allowableValues}} + {{/enumUnknownDefaultCase}} + } + + {{/isEnum}} + {{/allParams}} + /** + * {{summary}} + * {{notes}} + {{#allParams}} * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} + {{/allParams}} * @return {{{returnType}}}{{^returnType}}void{{/returnType}} + */ + {{#returnType}} + @Suppress("UNCHECKED_CAST") + {{/returnType}} + open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}> { + + val localVariableAuthNames = listOf({{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}}) + + val localVariableBody = {{#hasBodyParam}}{{#bodyParam}}{{#isArray}}{{operationIdCamelCase}}Request({{{paramName}}}{{^isList}}.asList(){{/isList}}){{/isArray}}{{^isArray}}{{#isMap}}{{operationIdCamelCase}}Request({{{paramName}}}){{/isMap}}{{^isMap}}{{{paramName}}}{{/isMap}}{{/isArray}}{{/bodyParam}}{{/hasBodyParam}} + {{^hasBodyParam}} + {{#hasFormParams}} + {{#isMultipart}} + formData { + {{#formParams}} + {{#isArray}} + {{{paramName}}}?.onEach { + append("{{{baseName}}}[]", it) + } + {{/isArray}} + {{^isArray}} + {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } + {{/isArray}} + {{/formParams}} + } + {{/isMultipart}} + {{^isMultipart}} + ParametersBuilder().also { + {{#formParams}} + {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}.toString()) } + {{/formParams}} + }.build() + {{/isMultipart}} + {{/hasFormParams}} + {{^hasFormParams}} + io.ktor.client.utils.EmptyContent + {{/hasFormParams}} + {{/hasBodyParam}} + + val localVariableQuery = mutableMapOf>(){{#queryParams}} + {{{paramName}}}?.apply { localVariableQuery["{{baseName}}"] = {{#isContainer}}toMultiValue(this, "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{^isEnum}}"${{{paramName}}}"{{/isEnum}}{{#isEnum}}"${ {{paramName}}.value }"{{/isEnum}}){{/isContainer}} }{{/queryParams}} + val localVariableHeaders = mutableMapOf(){{#headerParams}} + {{{paramName}}}?.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} }{{/headerParams}} + + val localVariableConfig = RequestConfig( + RequestMethod.{{httpMethod}}, + "{{path}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{^isEnum}}"${{{paramName}}}"{{/isEnum}}{{#isEnum}}"${ {{paramName}}.value }"{{/isEnum}}{{/isContainer}}){{/pathParams}}, + query = localVariableQuery, + headers = localVariableHeaders, + requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, + ) + + return {{#hasBodyParam}}jsonRequest{{/hasBodyParam}}{{^hasBodyParam}}{{#hasFormParams}}{{#isMultipart}}multipartFormRequest{{/isMultipart}}{{^isMultipart}}urlEncodedFormRequest{{/isMultipart}}{{/hasFormParams}}{{^hasFormParams}}request{{/hasFormParams}}{{/hasBodyParam}}( + localVariableConfig, + localVariableBody, + localVariableAuthNames + ).{{#isArray}}wrap<{{operationIdCamelCase}}Response>().map { value{{^isList}}.toTypedArray(){{/isList}} }{{/isArray}}{{^isArray}}{{#isMap}}wrap<{{operationIdCamelCase}}Response>().map { value }{{/isMap}}{{^isMap}}wrap(){{/isMap}}{{/isArray}} + } + +{{#hasBodyParam}} +{{#bodyParam}} +{{#isArray}}{{>serial_wrapper_request_list}}{{/isArray}}{{#isMap}}{{>serial_wrapper_request_map}}{{/isMap}} +{{/bodyParam}} +{{/hasBodyParam}} +{{#isArray}} +{{>serial_wrapper_response_list}} +{{/isArray}} +{{#isMap}} +{{>serial_wrapper_response_map}} +{{/isMap}} + + {{/operation}} +} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache new file mode 100644 index 000000000000..618fd7a88903 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache @@ -0,0 +1,16 @@ +package {{packageName}}.auth + +class ApiKeyAuth(private val location: String, val paramName: String) : Authentication { + var apiKey: String? = null + var apiKeyPrefix: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val key: String = apiKey ?: return + val prefix: String? = apiKeyPrefix + val value: String = if (prefix != null) "$prefix $key" else key + when (location) { + "query" -> query[paramName] = listOf(value) + "header" -> headers[paramName] = value + } + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache new file mode 100644 index 000000000000..1aab9156d98b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache @@ -0,0 +1,13 @@ +package {{packageName}}.auth + +interface Authentication { + + /** + * Apply authentication settings to header and query params. + * + * @param query Query parameters. + * @param headers Header parameters. + */ + fun apply(query: MutableMap>, headers: MutableMap) + +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache new file mode 100644 index 000000000000..26325424e5dd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache @@ -0,0 +1,17 @@ +package {{packageName}}.auth + +import io.ktor.util.InternalAPI +import io.ktor.util.encodeBase64 + +class HttpBasicAuth : Authentication { + var username: String? = null + var password: String? = null + + @OptIn(InternalAPI::class) + override fun apply(query: MutableMap>, headers: MutableMap) { + if (username == null && password == null) return + val str = (username ?: "") + ":" + (password ?: "") + val auth = str.encodeBase64() + headers["Authorization"] = "Basic $auth" + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache new file mode 100644 index 000000000000..982389d09609 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache @@ -0,0 +1,14 @@ +package {{packageName}}.auth + +class HttpBearerAuth(private val scheme: String?) : Authentication { + var bearerToken: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val token: String = bearerToken ?: return + headers["Authorization"] = (if (scheme != null) upperCaseBearer(scheme)!! + " " else "") + token + } + + private fun upperCaseBearer(scheme: String): String? { + return if ("bearer".equals(scheme, ignoreCase = true)) "Bearer" else scheme + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache new file mode 100644 index 000000000000..0945969b8f55 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache @@ -0,0 +1,10 @@ +package {{packageName}}.auth + +class OAuth : Authentication { + var accessToken: String? = null + + override fun apply(query: MutableMap>, headers: MutableMap) { + val token: String = accessToken ?: return + headers["Authorization"] = "Bearer $token" + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache new file mode 100644 index 000000000000..e527fb53d363 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache @@ -0,0 +1,103 @@ +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + +plugins { + kotlin("multiplatform"){{^omitGradlePluginVersions}} version "1.9.20" // kotlin_version{{/omitGradlePluginVersions}} + kotlin("plugin.serialization"){{^omitGradlePluginVersions}} version "1.9.20" // kotlin_version{{/omitGradlePluginVersions}} +} + +group = "{{groupId}}" +version = "{{artifactVersion}}" + +val kotlin_version = "1.9.20" +val coroutines_version = "1.7.3" +val serialization_version = "1.6.1" +val ktor_version = "2.3.6" + +repositories { + mavenCentral() +} + +kotlin { + jvm() + iosX64() + iosArm64() + iosSimulatorArm64() + js { + browser() + nodejs() + } + + sourceSets { + commonMain { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version") + + api("io.ktor:ktor-client-core:$ktor_version") + api("io.ktor:ktor-client-serialization:$ktor_version") + api("io.ktor:ktor-client-content-negotiation:$ktor_version") + api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version") + + {{#kotlinx-datetime}} + api("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1") + {{/kotlinx-datetime}} + } + } + + commonTest { + dependencies { + implementation(kotlin("test")) + implementation("io.ktor:ktor-client-mock:$ktor_version") + } + } + + jvmMain { + dependencies { + implementation(kotlin("stdlib-jdk7")) + implementation("io.ktor:ktor-client-cio-jvm:$ktor_version") + } + } + + jvmTest { + dependencies { + implementation(kotlin("test-junit")) + } + } + + iosMain { + dependencies { + api("io.ktor:ktor-client-ios:$ktor_version") + } + } + + jsMain { + dependencies { + api("io.ktor:ktor-client-js:$ktor_version") + } + } + + all { + languageSettings.apply { + optIn("kotlin.Experimental") + } + } + } +} + +tasks { + register("iosTest") { + val device = project.findProperty("device")?.toString() ?: "iPhone 8" + dependsOn("linkDebugTestIosX64") + group = JavaBasePlugin.VERIFICATION_GROUP + description = "Execute unit tests on ${device} simulator" + doLast { + val binary = kotlin.targets.getByName("iosX64").binaries.getTest("DEBUG") + exec { + commandLine("xcrun", "simctl", "spawn", device, binary.outputFile) + } + } + } + register("test") { + dependsOn("allTests") + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache new file mode 100644 index 000000000000..c3bd8b18461c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache @@ -0,0 +1,13 @@ +{{>licenseInfo}} + +package util + +import kotlinx.coroutines.CoroutineScope + +/** +* Block the current thread until execution of the given coroutine is complete. +* +* @param block The coroutine code. +* @return The result of the coroutine. +*/ +internal expect fun runTest(block: suspend CoroutineScope.() -> T): T diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache new file mode 100644 index 000000000000..a07f17e10b01 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,199 @@ +package {{packageName}}.infrastructure + +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.* +import io.ktor.client.request.forms.FormDataContent +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.header +import io.ktor.client.request.parameter +import io.ktor.client.statement.HttpResponse +import io.ktor.http.ContentType +import io.ktor.serialization.kotlinx.json.json +import io.ktor.http.* +import io.ktor.http.content.PartData +import io.ktor.http.contentType +import kotlin.Unit +import kotlinx.serialization.json.Json + +import {{packageName}}.auth.* + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient( + private val baseUrl: String +) { + + private lateinit var client: HttpClient + + constructor( + baseUrl: String, + httpClientEngine: HttpClientEngine?, + httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, + jsonBlock: Json, + ) : this(baseUrl = baseUrl) { + val clientConfig: (HttpClientConfig<*>) -> Unit by lazy { + { + it.install(ContentNegotiation) { json(jsonBlock) } + httpClientConfig?.invoke(it) + } + } + + client = httpClientEngine?.let { HttpClient(it, clientConfig) } ?: HttpClient(clientConfig) + } + + constructor( + baseUrl: String, + httpClient: HttpClient + ): this(baseUrl = baseUrl) { + this.client = httpClient + } + + {{#hasAuthMethods}} + private val authentications: kotlin.collections.Map by lazy { + mapOf({{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + "{{name}}" to HttpBasicAuth(){{/isBasicBasic}}{{#isBasicBearer}} + "{{name}}" to HttpBearerAuth("{{scheme}}"){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + "{{name}}" to ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"){{/isApiKey}}{{#isOAuth}} + "{{name}}" to OAuth(){{/isOAuth}}{{^-last}}, {{/-last}}{{/authMethods}}) + } + {{/hasAuthMethods}} + {{^hasAuthMethods}} + private val authentications: kotlin.collections.Map? = null + {{/hasAuthMethods}} + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + const val BASE_URL = "{{{basePath}}}" + val JSON_DEFAULT = Json { + ignoreUnknownKeys = true + prettyPrint = true + isLenient = true + } + protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType) + } + + /** + * Set the username for the first HTTP basic authentication. + * + * @param username Username + */ + fun setUsername(username: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? + ?: throw Exception("No HTTP basic authentication configured") + auth.username = username + } + + /** + * Set the password for the first HTTP basic authentication. + * + * @param password Password + */ + fun setPassword(password: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? + ?: throw Exception("No HTTP basic authentication configured") + auth.password = password + } + + /** + * Set the API key value for the first API key authentication. + * + * @param apiKey API key + * @param paramName The name of the API key parameter, or null or set the first key. + */ + fun setApiKey(apiKey: String, paramName: String? = null) { + val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth? + ?: throw Exception("No API key authentication configured") + auth.apiKey = apiKey + } + + /** + * Set the API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + * @param paramName The name of the API key parameter, or null or set the first key. + */ + fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) { + val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth? + ?: throw Exception("No API key authentication configured") + auth.apiKeyPrefix = apiKeyPrefix + } + + /** + * Set the access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + fun setAccessToken(accessToken: String) { + val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth? + ?: throw Exception("No OAuth2 authentication configured") + auth.accessToken = accessToken + } + + /** + * Set the access token for the first Bearer authentication. + * + * @param bearerToken The bearer token. + */ + fun setBearerToken(bearerToken: String) { + val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth? + ?: throw Exception("No Bearer authentication configured") + auth.bearerToken = bearerToken + } + + protected suspend fun multipartFormRequest(requestConfig: RequestConfig, body: kotlin.collections.List?, authNames: kotlin.collections.List): HttpResponse { + return request(requestConfig, MultiPartFormDataContent(body ?: listOf()), authNames) + } + + protected suspend fun urlEncodedFormRequest(requestConfig: RequestConfig, body: Parameters?, authNames: kotlin.collections.List): HttpResponse { + return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames) + } + + protected suspend fun jsonRequest(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse = request(requestConfig, body, authNames) + + protected suspend fun request(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse { + requestConfig.updateForAuth(authNames) + val headers = requestConfig.headers + + return client.request { + this.url { + this.takeFrom(URLBuilder(baseUrl)) + appendPath(requestConfig.path.trimStart('/').split('/')) + requestConfig.query.forEach { query -> + query.value.forEach { value -> + parameter(query.key, value) + } + } + } + this.method = requestConfig.method.httpMethod + headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) } + if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) { + val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) } + ?: ContentType.Application.Json) + this.contentType(contentType) + this.setBody(body) + } + } + } + + private fun RequestConfig.updateForAuth(authNames: kotlin.collections.List) { + for (authName in authNames) { + val auth = authentications?.get(authName) ?: throw Exception("Authentication undefined: $authName") + auth.apply(query, headers) + } + } + + private fun URLBuilder.appendPath(components: kotlin.collections.List): URLBuilder = apply { + encodedPath = encodedPath.trimEnd('/') + components.joinToString("/", prefix = "/") { it.encodeURLQueryComponent() } + } + + private val RequestMethod.httpMethod: HttpMethod + get() = when (this) { + RequestMethod.DELETE -> HttpMethod.Delete + RequestMethod.GET -> HttpMethod.Get + RequestMethod.HEAD -> HttpMethod.Head + RequestMethod.PATCH -> HttpMethod.Patch + RequestMethod.PUT -> HttpMethod.Put + RequestMethod.POST -> HttpMethod.Post + RequestMethod.OPTIONS -> HttpMethod.Options + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache new file mode 100644 index 000000000000..b8bb160990ae --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache @@ -0,0 +1,29 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +@Serializable(Base64ByteArray.Companion::class) +class Base64ByteArray(val value: ByteArray) { + companion object : KSerializer { + override val descriptor = PrimitiveSerialDescriptor("Base64ByteArray", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, obj: Base64ByteArray) = encoder.encodeString(obj.value.encodeBase64()) + override fun deserialize(decoder: Decoder) = Base64ByteArray(decoder.decodeString().decodeBase64Bytes()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + other as Base64ByteArray + return value.contentEquals(other.value) + } + + override fun hashCode(): Int { + return value.contentHashCode() + } + + override fun toString(): String { + return "Base64ByteArray(${hex(value)})" + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache new file mode 100644 index 000000000000..a11e0525d910 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache @@ -0,0 +1,101 @@ +package {{packageName}}.infrastructure + +import io.ktor.utils.io.core.* +import kotlin.experimental.and + +private val digits = "0123456789abcdef".toCharArray() +private const val BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +private const val BASE64_MASK: Byte = 0x3f +private const val BASE64_PAD = '=' +private val BASE64_INVERSE_ALPHABET = IntArray(256) { BASE64_ALPHABET.indexOf(it.toChar()) } + +private fun String.toCharArray(): CharArray = CharArray(length) { get(it) } +private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it] = 0 } +private fun Int.toBase64(): Char = BASE64_ALPHABET[this] +private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK +internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64() +internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeText(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes() + +/** + * Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes. + * + * Taken from https://github.com/ktorio/ktor/blob/master/ktor-utils/common/src/io/ktor/util/Crypto.kt + */ +internal fun hex(bytes: ByteArray): String { + val result = CharArray(bytes.size * 2) + var resultIndex = 0 + val digits = digits + + for (element in bytes) { + val b = element.toInt() and 0xff + result[resultIndex++] = digits[b shr 4] + result[resultIndex++] = digits[b and 0x0f] + } + + return result.concatToString() +} + +/** + * Decode bytes from HEX string. It should be no spaces and `0x` prefixes. + * + * Taken from https://github.com/ktorio/ktor/blob/master/ktor-utils/common/src/io/ktor/util/Crypto.kt + */ +internal fun hex(s: String): ByteArray { + val result = ByteArray(s.length / 2) + for (idx in result.indices) { + val srcIdx = idx * 2 + val high = s[srcIdx].toString().toInt(16) shl 4 + val low = s[srcIdx + 1].toString().toInt(16) + result[idx] = (high or low).toByte() + } + + return result +} + +/** + * Encode [ByteReadPacket] in base64 format. + * + * Taken from https://github.com/ktorio/ktor/blob/424d1d2cfaa3281302c60af9500f738c8c2fc846/ktor-utils/common/src/io/ktor/util/Base64.kt + */ +private fun ByteReadPacket.encodeBase64(): String = buildString { + val data = ByteArray(3) + while (remaining > 0) { + val read = readAvailable(data) + data.clearFrom(read) + + val padSize = (data.size - read) * 8 / 6 + val chunk = ((data[0].toInt() and 0xFF) shl 16) or + ((data[1].toInt() and 0xFF) shl 8) or + (data[2].toInt() and 0xFF) + + for (index in data.size downTo padSize) { + val char = (chunk shr (6 * index)) and BASE64_MASK.toInt() + append(char.toBase64()) + } + + repeat(padSize) { append(BASE64_PAD) } + } +} + +/** + * Decode [ByteReadPacket] from base64 format + * + * Taken from https://github.com/ktorio/ktor/blob/424d1d2cfaa3281302c60af9500f738c8c2fc846/ktor-utils/common/src/io/ktor/util/Base64.kt + */ +@Suppress("DEPRECATION") +private fun ByteReadPacket.decodeBase64Bytes(): Input = buildPacket { + val data = ByteArray(4) + + while (remaining > 0) { + val read = readAvailable(data) + + val chunk = data.foldIndexed(0) { index, result, current -> + result or (current.fromBase64().toInt() shl ((3 - index) * 6)) + } + + for (index in data.size - 2 downTo (data.size - read)) { + val origin = (chunk shr (8 * index)) and 0xff + writeByte(origin.toByte()) + } + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache new file mode 100644 index 000000000000..87a68f3084e5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache @@ -0,0 +1,51 @@ +package {{packageName}}.infrastructure + +import io.ktor.http.Headers +import io.ktor.http.isSuccess +import io.ktor.util.reflect.TypeInfo +import io.ktor.util.reflect.typeInfo + +{{#nonPublicApi}}internal {{/nonPublicApi}}open class HttpResponse(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider) { + val status: Int = response.status.value + val success: Boolean = response.status.isSuccess() + val headers: Map> = response.headers.mapEntries() + suspend fun body(): T = provider.body(response) + suspend fun typedBody(type: TypeInfo): V = provider.typedBody(response, type) + + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { + private fun Headers.mapEntries(): Map> { + val result = mutableMapOf>() + entries().forEach { result[it.key] = it.value } + return result + } + } +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}interface BodyProvider { + suspend fun body(response: io.ktor.client.statement.HttpResponse): T + suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider(private val type: TypeInfo) : BodyProvider { + @Suppress("UNCHECKED_CAST") + override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = + response.call.body(type) as T + + @Suppress("UNCHECKED_CAST") + override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = + response.call.body(type) as V +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider(private val provider: BodyProvider, private val block: S.() -> T) : BodyProvider { + override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = + block(provider.body(response)) + + override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = + provider.typedBody(response, type) +} + +{{#nonPublicApi}}internal {{/nonPublicApi}}inline fun io.ktor.client.statement.HttpResponse.wrap(): HttpResponse = + HttpResponse(this, TypedBodyProvider(typeInfo())) + +{{#nonPublicApi}}internal {{/nonPublicApi}}fun HttpResponse.map(block: T.() -> V): HttpResponse = + HttpResponse(response, MappedBodyProvider(provider, block)) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache new file mode 100644 index 000000000000..80c85b51c723 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache @@ -0,0 +1,29 @@ +package {{packageName}}.infrastructure + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +@Serializable(OctetByteArray.Companion::class) +class OctetByteArray(val value: ByteArray) { + companion object : KSerializer { + override val descriptor = PrimitiveSerialDescriptor("OctetByteArray", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, obj: OctetByteArray) = encoder.encodeString(hex(obj.value)) + override fun deserialize(decoder: Decoder) = OctetByteArray(hex(decoder.decodeString())) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + other as OctetByteArray + return value.contentEquals(other.value) + } + + override fun hashCode(): Int { + return value.contentHashCode() + } + + override fun toString(): String { + return "OctetByteArray(${hex(value)})" + } +} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache new file mode 100644 index 000000000000..351c0120b7b1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache @@ -0,0 +1,8 @@ +{{>licenseInfo}} + +package util + +import kotlinx.coroutines.CoroutineScope +import kotlin.coroutines.EmptyCoroutineContext + +internal actual fun runTest(block: suspend CoroutineScope.() -> T): T = kotlinx.coroutines.runBlocking(EmptyCoroutineContext, block) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache new file mode 100644 index 000000000000..23f710816de7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache @@ -0,0 +1,7 @@ +package util + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise + +actual fun runTest(block: suspend (scope : CoroutineScope) -> T): dynamic = GlobalScope.promise { block(this) } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache new file mode 100644 index 000000000000..351c0120b7b1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache @@ -0,0 +1,8 @@ +{{>licenseInfo}} + +package util + +import kotlinx.coroutines.CoroutineScope +import kotlin.coroutines.EmptyCoroutineContext + +internal actual fun runTest(block: suspend CoroutineScope.() -> T): T = kotlinx.coroutines.runBlocking(EmptyCoroutineContext, block) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache new file mode 100644 index 000000000000..179205c3fece --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache @@ -0,0 +1,64 @@ + + 4.0.0 + {{groupId}} + {{artifactId}} + {{artifactVersion}} + {{appName}} + pom + + UTF-8 + + + + + maven-dependency-plugin + + + package + + copy-dependencies + + + ${project.build.directory} + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + bundle-clean + clean + + exec + + + /bin/bash + + gradlew + clean + + + + + bundle-test + integration-test + + exec + + + /bin/bash + + gradlew + build + + + + + + + + diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache new file mode 100644 index 000000000000..358dfa70be50 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache @@ -0,0 +1,9 @@ + @Serializable({{operationIdCamelCase}}Request.Companion::class) + private class {{operationIdCamelCase}}Request(val value: List<{{#bodyParam}}{{baseType}}{{/bodyParam}}>) { + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Request> { + private val serializer: KSerializer> = serializer>() + override val descriptor = serializer.descriptor + override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Request) = serializer.serialize(encoder, obj.value) + override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Request(serializer.deserialize(decoder)) + } + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache new file mode 100644 index 000000000000..55efd01ad655 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache @@ -0,0 +1,9 @@ + @Serializable({{operationIdCamelCase}}Request.Companion::class) + private class {{operationIdCamelCase}}Request(val value: Map) { + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Request> { + private val serializer: KSerializer> = serializer>() + override val descriptor = serializer.descriptor + override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Request) = serializer.serialize(encoder, obj.value) + override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Request(serializer.deserialize(decoder)) + } + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache new file mode 100644 index 000000000000..3f191db3a67b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache @@ -0,0 +1,9 @@ + @Serializable({{operationIdCamelCase}}Response.Companion::class) + private class {{operationIdCamelCase}}Response(val value: List<{{returnBaseType}}>) { + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Response> { + private val serializer: KSerializer> = serializer>() + override val descriptor = serializer.descriptor + override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Response) = serializer.serialize(encoder, obj.value) + override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Response(serializer.deserialize(decoder)) + } + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache new file mode 100644 index 000000000000..bbd342cfa1a8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache @@ -0,0 +1,9 @@ + @Serializable({{operationIdCamelCase}}Response.Companion::class) + private class {{operationIdCamelCase}}Response(val value: Map) { + {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Response> { + private val serializer: KSerializer> = serializer>() + override val descriptor = serializer.descriptor + override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Response) = serializer.serialize(encoder, obj.value) + override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Response(serializer.deserialize(decoder)) + } + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache new file mode 100644 index 000000000000..ba0114cb1764 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache @@ -0,0 +1 @@ +rootProject.name = "{{artifactId}}" diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache new file mode 100644 index 000000000000..d11e8934fe76 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache @@ -0,0 +1,14 @@ +/** + * + * Please note: + * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * Do not edit this file manually. + * + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport" +) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache new file mode 100644 index 000000000000..831a0e79b934 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache @@ -0,0 +1,16 @@ +{{>licenseInfo}} +package {{modelPackage}} + +{{#imports}}import {{import}} +{{/imports}} + +{{#models}} +{{#model}} +{{^generateOneOfAnyOfWrappers}} +{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{#isAlias}}typealias {{classname}} = {{{dataType}}}{{/isAlias}}{{^isAlias}}{{>data_class}}{{/isAlias}}{{/isEnum}} +{{/generateOneOfAnyOfWrappers}} +{{#generateOneOfAnyOfWrappers}} +{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{#isAlias}}typealias {{classname}} = {{{dataType}}}{{/isAlias}}{{^isAlias}}{{#oneOf}}{{#-first}}{{>oneof_class}}{{/-first}}{{/oneOf}}{{#anyOf}}{{#-first}}{{>anyof_class}}{{/-first}}{{/anyOf}}{{^oneOf}}{{^anyOf}}{{>data_class}}{{/anyOf}}{{/oneOf}}{{/isAlias}}{{/isEnum}} +{{/generateOneOfAnyOfWrappers}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache new file mode 100644 index 000000000000..4c7f39007171 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache @@ -0,0 +1 @@ +{{#modelMutable}}var{{/modelMutable}}{{^modelMutable}}val{{/modelMutable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache new file mode 100644 index 000000000000..e3b718421188 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache @@ -0,0 +1,3 @@ +{{#models}}{{#model}} +{{#isEnum}}{{>enum_doc}}{{/isEnum}}{{^isEnum}}{{>class_doc}}{{/isEnum}} +{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache new file mode 100644 index 000000000000..bc442d04ea6b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache @@ -0,0 +1,38 @@ +{{>licenseInfo}} +package {{roomModelPackage}} + +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import {{modelPackage}}.* + +{{#models}} +{{#model}} + +@Entity(tableName = "{{classname}}") +/** +* Room model for {{{description}}} +{{#allVars}} +* @param {{{name}}} {{{description}}} +{{/allVars}} +*/ +data class {{classname}}RoomModel ( + @PrimaryKey(autoGenerate = true) var roomTableId: Int, + {{#allVars}}{{#items.isPrimitiveType}}var {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{>model_room_init_var}}{{/isArray}}, + {{/items.isPrimitiveType}}{{/allVars}} + {{#allVars}}{{^isEnum}}{{^isArray}}var {{{name}}}: {{{dataType}}}{{>model_room_init_var}}, + {{/isArray}}{{/isEnum}}{{/allVars}} + {{#allVars}}{{#isEnum}}{{^isArray}}var {{{name}}}: {{classname}}.{{{nameInPascalCase}}}{{>model_room_init_var}}, + {{/isArray}}{{/isEnum}}{{/allVars}}) { +{{#allVars}}{{#isArray}}{{#isList}}{{^items.isPrimitiveType}} + @Ignore + {{^isNullable}}{{#required}}lateinit {{/required}}{{/isNullable}}var {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{>model_room_init_var}}{{/isArray}} +{{/items.isPrimitiveType}}{{/isList}}{{/isArray}}{{/allVars}} + {{^discriminator}}companion object { } + + fun toApiModel(): {{classname}} = {{classname}}( + {{#allVars}}{{name}} = this.{{name}}, + {{/allVars}}){{/discriminator}} +} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache new file mode 100644 index 000000000000..3e6f299d3d6e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache @@ -0,0 +1 @@ +{{#isNullable}}?{{/isNullable}}{{^required}}{{^isNullable}}?{{/isNullable}}{{/required}}{{#defaultvalue}} = {{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}{{#isNullable}} = null{{/isNullable}}{{^required}}{{^isNullable}} = null{{/isNullable}}{{/required}}{{/defaultvalue}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache new file mode 100644 index 000000000000..c13e2a054a43 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache @@ -0,0 +1,29 @@ +{{>licenseInfo}} +package {{modelPackage}} + +import io.kotlintest.shouldBe +import io.kotlintest.specs.ShouldSpec + +import {{modelPackage}}.{{{classname}}} +{{#imports}}import {{import}} +{{/imports}} + +{{#models}} +{{#model}} +class {{{classname}}}Test : ShouldSpec() { + init { + // uncomment below to create an instance of {{{classname}}} + //val modelInstance = {{{classname}}}() + + {{#vars}} + // to test the property `{{{name}}}`{{#description}} - {{{.}}}{{/description}} + should("test {{{name}}}") { + // uncomment below to test the property + //modelInstance.{{{name}}} shouldBe ("TODO") + } + + {{/vars}} + } +} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache new file mode 100644 index 000000000000..eebf9858773b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache @@ -0,0 +1,239 @@ +{{^multiplatform}} +{{#gson}} +{{#generateOneOfAnyOfWrappers}} +import com.google.gson.Gson +import com.google.gson.JsonElement +import com.google.gson.TypeAdapter +import com.google.gson.TypeAdapterFactory +import com.google.gson.reflect.TypeToken +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.google.gson.annotations.JsonAdapter +{{/generateOneOfAnyOfWrappers}} +import com.google.gson.annotations.SerializedName +{{/gson}} +{{#moshi}} +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +{{/moshi}} +{{#jackson}} +{{#enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue +{{/enumUnknownDefaultCase}} +import com.fasterxml.jackson.annotation.JsonProperty +{{#discriminator}} +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo +{{/discriminator}} +{{/jackson}} +{{#kotlinx_serialization}} +import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} +import kotlinx.serialization.SerialName +import kotlinx.serialization.Contextual +{{#enumUnknownDefaultCase}} +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +{{/enumUnknownDefaultCase}} +{{#hasEnums}} +{{/hasEnums}} +{{/kotlinx_serialization}} +{{#parcelizeModels}} +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +{{/parcelizeModels}} +{{/multiplatform}} +{{#multiplatform}} +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +{{/multiplatform}} +{{#serializableModel}} +import java.io.Serializable +{{/serializableModel}} +{{#generateRoomModels}} +import {{roomModelPackage}}.{{classname}}RoomModel +import {{packageName}}.infrastructure.ITransformForStorage +{{/generateRoomModels}} +import java.io.IOException + +/** + * {{{description}}} + * + */ +{{#parcelizeModels}} +@Parcelize +{{/parcelizeModels}} +{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} +{{#isDeprecated}} +@Deprecated(message = "This schema is deprecated.") +{{/isDeprecated}} +{{>additionalModelTypeAnnotations}} +{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}(var actualInstance: Any? = null) { + + class CustomTypeAdapterFactory : TypeAdapterFactory { + override fun create(gson: Gson, type: TypeToken): TypeAdapter? { + if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { + return null // this class only serializes '{{classname}}' and its subtypes + } + val elementAdapter = gson.getAdapter(JsonElement::class.java) + {{#composedSchemas}} + {{#oneOf}} + {{^isArray}} + {{^vendorExtensions.x-duplicated-data-type}} + val adapter{{{dataType}}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}::class.java)) + {{/vendorExtensions.x-duplicated-data-type}} + {{/isArray}} + {{#isArray}} + @Suppress("UNCHECKED_CAST") + val adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = gson.getDelegateAdapter(this, TypeToken.get(object : TypeToken<{{{dataType}}}>() {}.type)) as TypeAdapter<{{{dataType}}}> + {{/isArray}} + {{/oneOf}} + {{/composedSchemas}} + + @Suppress("UNCHECKED_CAST") + return object : TypeAdapter<{{classname}}?>() { + @Throws(IOException::class) + override fun write(out: JsonWriter,value: {{classname}}?) { + if (value?.actualInstance == null) { + elementAdapter.write(out, null) + return + } + + {{#composedSchemas}} + {{#oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + // check if the actual instance is of the type `{{{dataType}}}` + if (value.actualInstance is {{#isArray}}List<*>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) { + {{#isArray}} + val list = value.actualInstance as List + if (list.get(0) is {{{items.dataType}}}) { + val array = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonArray() + elementAdapter.write(out, array) + return + } + {{/isArray}} + {{^isArray}} + {{#isPrimitiveType}} + val primitive = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonPrimitive() + elementAdapter.write(out, primitive) + return + {{/isPrimitiveType}} + {{^isPrimitiveType}} + val element = adapter{{{dataType}}}.toJsonTree(value.actualInstance as {{{dataType}}}?) + elementAdapter.write(out, element) + return + {{/isPrimitiveType}} + {{/isArray}} + } + {{/vendorExtensions.x-duplicated-data-type}} + {{/oneOf}} + {{/composedSchemas}} + throw IOException("Failed to serialize as the type doesn't match oneOf schemas: {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}") + } + + @Throws(IOException::class) + override fun read(jsonReader: JsonReader): {{classname}} { + val jsonElement = elementAdapter.read(jsonReader) + var match = 0 + val errorMessages = ArrayList() + var actualAdapter: TypeAdapter<*> = elementAdapter + + {{#composedSchemas}} + {{#oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + {{^hasVars}} + // deserialize {{{dataType}}} + try { + // validate the JSON object to see if any exception is thrown + {{^isArray}} + {{#isNumber}} + require(jsonElement.getAsJsonPrimitive().isNumber()) { + String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) + } + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + {{/isNumber}} + {{^isNumber}} + {{#isPrimitiveType}} + require(jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { + String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) + } + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + {{/isPrimitiveType}} + {{/isNumber}} + {{^isNumber}} + {{^isPrimitiveType}} + {{{dataType}}}.validateJsonElement(jsonElement) + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + {{/isPrimitiveType}} + {{/isNumber}} + {{/isArray}} + {{#isArray}} + require(jsonElement.isJsonArray) { + String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()) + } + + // validate array items + for(element in jsonElement.getAsJsonArray()) { + {{#items}} + {{#isNumber}} + require(jsonElement.getAsJsonPrimitive().isNumber) { + String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) + } + {{/isNumber}} + {{^isNumber}} + {{#isPrimitiveType}} + require(element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}) { + String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) + } + {{/isPrimitiveType}} + {{/isNumber}} + {{^isNumber}} + {{^isPrimitiveType}} + {{{dataType}}}.validateJsonElement(element) + {{/isPrimitiveType}} + {{/isNumber}} + {{/items}} + } + actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} + {{/isArray}} + match++ + //log.log(Level.FINER, "Input data matches schema '{{{dataType}}}'") + } catch (e: Exception) { + // deserialization failed, continue + errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.message)) + //log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e) + } + {{/hasVars}} + {{#hasVars}} + // deserialize {{{.}}} + try { + // validate the JSON object to see if any exception is thrown + {{.}}.validateJsonElement(jsonElement) + actualAdapter = adapter{{.}} + match++ + //log.log(Level.FINER, "Input data matches schema '{{{.}}}'") + } catch (e: Exception) { + // deserialization failed, continue + errorMessages.add(String.format("Deserialization for {{{.}}} failed with `%s`.", e.message)) + //log.log(Level.FINER, "Input data does not match schema '{{{.}}}'", e) + } + {{/hasVars}} + {{/vendorExtensions.x-duplicated-data-type}} + {{/oneOf}} + {{/composedSchemas}} + + if (match == 1) { + val ret = {{classname}}() + ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) + return ret + } + + throw IOException(String.format("Failed deserialization for {{classname}}: %d classes match result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", match, errorMessages, jsonElement.toString())) + } + }.nullSafe() as TypeAdapter + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache new file mode 100644 index 000000000000..1639fdc5374e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache @@ -0,0 +1 @@ +{{^isNumber}}{{#isEnum}}{{#enumDefaultValue}}{{enumName}}{{operationIdCamelCase}}.{{.}}{{/enumDefaultValue}}{{^enumDefaultValue}}null{{/enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache new file mode 100644 index 000000000000..cc08101c2a29 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache @@ -0,0 +1 @@ +rootProject.name = '{{artifactId}}' diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache new file mode 100644 index 000000000000..6b3cb83b0f8e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache @@ -0,0 +1,6 @@ +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminator.propertyBaseName}}}", visible = true) +@JsonSubTypes( +{{#discriminator.mappedModels}} + JsonSubTypes.Type(value = {{modelName}}::class, name = "{{^vendorExtensions.x-discriminator-value}}{{mappingName}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}"){{^-last}},{{/-last}} +{{/discriminator.mappedModels}} +) \ No newline at end of file From bc59615c92904096b15cdf5267a4ccb946b95d57 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Fri, 4 Oct 2024 07:54:25 +0200 Subject: [PATCH 02/13] Generate working sealed classes from oneOf --- .../languages/TidalKotlinClientCodegen.java | 420 ++++++++++-------- .../tidal-kotlin/data_class.mustache | 12 +- 2 files changed, 247 insertions(+), 185 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java index 928b35736b5b..e64b16f3c1a2 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java @@ -17,42 +17,25 @@ package org.openapitools.codegen.languages; -import java.io.File; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import com.samskivert.mustache.Mustache; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.StringUtils; -import org.openapitools.codegen.CliOption; -import org.openapitools.codegen.CodegenConstants; -import org.openapitools.codegen.CodegenModel; -import org.openapitools.codegen.CodegenOperation; -import org.openapitools.codegen.CodegenParameter; -import org.openapitools.codegen.CodegenProperty; -import org.openapitools.codegen.CodegenType; -import org.openapitools.codegen.SupportingFile; -import org.openapitools.codegen.meta.features.ClientModificationFeature; -import org.openapitools.codegen.meta.features.DocumentationFeature; -import org.openapitools.codegen.meta.features.GlobalFeature; -import org.openapitools.codegen.meta.features.ParameterFeature; -import org.openapitools.codegen.meta.features.SchemaSupportFeature; -import org.openapitools.codegen.meta.features.SecurityFeature; -import org.openapitools.codegen.meta.features.WireFormatFeature; +import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.model.OperationMap; import org.openapitools.codegen.model.OperationsMap; -import org.openapitools.codegen.utils.ProcessUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import static java.util.Collections.sort; public class TidalKotlinClientCodegen extends AbstractKotlinCodegen { @@ -97,26 +80,35 @@ public class TidalKotlinClientCodegen extends AbstractKotlinCodegen { protected static final String VENDOR_EXTENSION_BASE_NAME_LITERAL = "x-base-name-literal"; - @Setter protected String dateLibrary = DateLibrary.JAVA8.value; - @Setter protected String requestDateConverter = RequestDateConverter.TO_JSON.value; - @Setter protected String collectionType = CollectionType.LIST.value; + @Setter + protected String dateLibrary = DateLibrary.JAVA8.value; + @Setter + protected String requestDateConverter = RequestDateConverter.TO_JSON.value; + @Setter + protected String collectionType = CollectionType.LIST.value; protected boolean useRxJava3 = false; protected boolean useCoroutines = false; // backwards compatibility for openapi configs that specify neither rx1 nor rx2 // (mustache does not allow for boolean operators so we need this extra field) protected boolean doNotUseRxAndCoroutines = true; protected boolean generateRoomModels = false; - @Setter protected String roomModelPackage = ""; - @Setter protected boolean omitGradleWrapper = false; - @Setter protected boolean generateOneOfAnyOfWrappers = true; - @Getter @Setter protected boolean failOnUnknownProperties = false; + @Setter + protected String roomModelPackage = ""; + @Setter + protected boolean omitGradleWrapper = false; + @Setter + protected boolean generateOneOfAnyOfWrappers = true; + @Getter + @Setter + protected boolean failOnUnknownProperties = false; protected String authFolder; - @Getter protected SERIALIZATION_LIBRARY_TYPE serializationLibrary = SERIALIZATION_LIBRARY_TYPE.moshi; + @Getter + protected SERIALIZATION_LIBRARY_TYPE serializationLibrary = SERIALIZATION_LIBRARY_TYPE.kotlinx_serialization; public static final String SERIALIZATION_LIBRARY_DESC = "What serialization library to use: 'moshi' (default), or 'gson' or 'jackson' or 'kotlinx_serialization'"; - public enum SERIALIZATION_LIBRARY_TYPE {moshi, gson, jackson, kotlinx_serialization} + public enum SERIALIZATION_LIBRARY_TYPE {moshi, jackson, kotlinx_serialization} public enum DateLibrary { STRING("string"), @@ -165,27 +157,27 @@ public TidalKotlinClientCodegen() { * OAuth flows supported _only_ by client explicitly setting bearer token. The "flows" are not supported. */ modifyFeatureSet(features -> features - .includeDocumentationFeatures(DocumentationFeature.Readme) - .excludeWireFormatFeatures(WireFormatFeature.XML, WireFormatFeature.PROTOBUF) - .securityFeatures(EnumSet.of( - SecurityFeature.BasicAuth, - SecurityFeature.ApiKey, - SecurityFeature.BearerToken, - SecurityFeature.OAuth2_AuthorizationCode,//retrofit only - SecurityFeature.OAuth2_Implicit)) - .excludeGlobalFeatures( - GlobalFeature.XMLStructureDefinitions, - GlobalFeature.Callbacks, - GlobalFeature.LinkObjects, - GlobalFeature.ParameterStyling - ) - .excludeSchemaSupportFeatures( - SchemaSupportFeature.Polymorphism - ) - .excludeParameterFeatures( - ParameterFeature.Cookie - ) - .includeClientModificationFeatures(ClientModificationFeature.BasePath) + .includeDocumentationFeatures(DocumentationFeature.Readme) + .excludeWireFormatFeatures(WireFormatFeature.XML, WireFormatFeature.PROTOBUF) + .securityFeatures(EnumSet.of( + SecurityFeature.BasicAuth, + SecurityFeature.ApiKey, + SecurityFeature.BearerToken, + SecurityFeature.OAuth2_AuthorizationCode,//retrofit only + SecurityFeature.OAuth2_Implicit)) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .excludeSchemaSupportFeatures( +// SchemaSupportFeature.Polymorphism + ) + .excludeParameterFeatures( + ParameterFeature.Cookie + ) + .includeClientModificationFeatures(ClientModificationFeature.BasePath) ); artifactId = "tidal-kotlin-client"; @@ -195,17 +187,17 @@ public TidalKotlinClientCodegen() { updateOption(CodegenConstants.ARTIFACT_ID, this.artifactId); updateOption(CodegenConstants.PACKAGE_NAME, this.packageName); - outputFolder = "generated-code" + File.separator + "kotlin-client"; + outputFolder = "generated-code" + File.separator + "tidal-kotlin"; modelTemplateFiles.put("model.mustache", ".kt"); - if (generateRoomModels) { - modelTemplateFiles.put("model_room.mustache", ".kt"); - } +// if (generateRoomModels) { +// modelTemplateFiles.put("model_room.mustache", ".kt"); +// } apiTemplateFiles.put("api.mustache", ".kt"); apiTestTemplateFiles.put("api_test.mustache", ".kt"); modelTestTemplateFiles.put("model_test.mustache", ".kt"); modelDocTemplateFiles.put("model_doc.mustache", ".md"); apiDocTemplateFiles.put("api_doc.mustache", ".md"); - embeddedTemplateDir = templateDir = "kotlin-client"; + embeddedTemplateDir = templateDir = "tidal-kotlin"; apiPackage = packageName + ".apis"; modelPackage = packageName + ".models"; @@ -229,20 +221,20 @@ public TidalKotlinClientCodegen() { collectionType.setDefault(this.collectionType); cliOptions.add(collectionType); - supportedLibraries.put(JVM_KTOR, "Platform: Java Virtual Machine. HTTP client: Ktor 1.6.7. JSON processing: Gson, Jackson (default)."); - supportedLibraries.put(JVM_OKHTTP4, "[DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0."); - supportedLibraries.put(JVM_SPRING_WEBCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 5 (or 6 with useSpringBoot3 enabled) WebClient. JSON processing: Jackson."); - supportedLibraries.put(JVM_SPRING_RESTCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 6 RestClient. JSON processing: Jackson."); +// supportedLibraries.put(JVM_KTOR, "Platform: Java Virtual Machine. HTTP client: Ktor 1.6.7. JSON processing: Gson, Jackson (default)."); +// supportedLibraries.put(JVM_OKHTTP4, "[DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0."); +// supportedLibraries.put(JVM_SPRING_WEBCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 5 (or 6 with useSpringBoot3 enabled) WebClient. JSON processing: Jackson."); +// supportedLibraries.put(JVM_SPRING_RESTCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 6 RestClient. JSON processing: Jackson."); supportedLibraries.put(JVM_RETROFIT2, "Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2."); supportedLibraries.put(MULTIPLATFORM, "Platform: Kotlin multiplatform. HTTP client: Ktor 1.6.7. JSON processing: Kotlinx Serialization: 1.2.1."); - supportedLibraries.put(JVM_VOLLEY, "Platform: JVM for Android. HTTP client: Volley 1.2.1. JSON processing: gson 2.8.9"); - supportedLibraries.put(JVM_VERTX, "Platform: Java Virtual Machine. HTTP client: Vert.x Web Client. JSON processing: Moshi, Gson or Jackson."); +// supportedLibraries.put(JVM_VOLLEY, "Platform: JVM for Android. HTTP client: Volley 1.2.1. JSON processing: gson 2.8.9"); +// supportedLibraries.put(JVM_VERTX, "Platform: Java Virtual Machine. HTTP client: Vert.x Web Client. JSON processing: Moshi, Gson or Jackson."); CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "Library template (sub-template) to use"); libraryOption.setEnum(supportedLibraries); libraryOption.setDefault(JVM_OKHTTP4); cliOptions.add(libraryOption); - setLibrary(JVM_OKHTTP4); + setLibrary(JVM_RETROFIT2); CliOption requestDateConverter = new CliOption(REQUEST_DATE_CONVERTER, "JVM-Option. Defines in how to handle date-time objects that are used for a request (as query or parameter)"); Map requestDateConverterOptions = new HashMap<>(); @@ -252,20 +244,20 @@ public TidalKotlinClientCodegen() { requestDateConverter.setDefault(this.requestDateConverter); cliOptions.add(requestDateConverter); - cliOptions.add(CliOption.newBoolean(USE_RX_JAVA3, "Whether to use the RxJava3 adapter with the retrofit2 library.")); +// cliOptions.add(CliOption.newBoolean(USE_RX_JAVA3, "Whether to use the RxJava3 adapter with the retrofit2 library.")); cliOptions.add(CliOption.newBoolean(USE_COROUTINES, "Whether to use the Coroutines adapter with the retrofit2 library.")); - cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT3, "Whether to use the Spring Boot 3 with the jvm-spring-webclient library.")); +// cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT3, "Whether to use the Spring Boot 3 with the jvm-spring-webclient library.")); cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_PLUGIN_VERSIONS, "Whether to declare Gradle plugin versions in build files.")); - cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_WRAPPER, "Whether to omit Gradle wrapper for creating a sub project.")); - cliOptions.add(CliOption.newBoolean(USE_SETTINGS_GRADLE, "Whether the project uses settings.gradle.")); - cliOptions.add(CliOption.newBoolean(IDEA, "Add IntellJ Idea plugin and mark Kotlin main and test folders as source folders.")); +// cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_WRAPPER, "Whether to omit Gradle wrapper for creating a sub project.")); +// cliOptions.add(CliOption.newBoolean(USE_SETTINGS_GRADLE, "Whether the project uses settings.gradle.")); +// cliOptions.add(CliOption.newBoolean(IDEA, "Add IntellJ Idea plugin and mark Kotlin main and test folders as source folders.")); - cliOptions.add(CliOption.newBoolean(MOSHI_CODE_GEN, "Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info.")); - cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", false)); +// cliOptions.add(CliOption.newBoolean(MOSHI_CODE_GEN, "Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info.")); +// cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", false)); cliOptions.add(CliOption.newBoolean(NULLABLE_RETURN_TYPE, "Nullable return type")); - cliOptions.add(CliOption.newBoolean(GENERATE_ROOM_MODELS, "Generate Android Room database models in addition to API models (JVM Volley library only)", false)); +// cliOptions.add(CliOption.newBoolean(GENERATE_ROOM_MODELS, "Generate Android Room database models in addition to API models (JVM Volley library only)", false)); cliOptions.add(CliOption.newBoolean(SUPPORT_ANDROID_API_LEVEL_25_AND_BELLOW, "[WARNING] This flag will generate code that has a known security vulnerability. It uses `kotlin.io.createTempFile` instead of `java.nio.file.Files.createTempFile` in order to support Android API level 25 and bellow. For more info, please check the following links https://github.com/OpenAPITools/openapi-generator/security/advisories/GHSA-23x4-m842-fmwf, https://github.com/OpenAPITools/openapi-generator/pull/9284")); @@ -282,12 +274,12 @@ public CodegenType getTag() { @Override public String getName() { - return "kotlin"; + return "tidal-kotlin"; } @Override public String getHelp() { - return "Generates a Kotlin client."; + return "Generates a Kotlin client for our beloved TIDAL overlords."; } public boolean getGenerateRoomModels() { @@ -334,7 +326,7 @@ public void setUseCoroutines(boolean useCoroutines) { * Sets the serialization engine for Kotlin * * @param enumSerializationLibrary The string representation of the serialization library as defined by - * {@link org.openapitools.codegen.languages.TidalKotlinClientCodegen.SERIALIZATION_LIBRARY_TYPE} + * {@link SERIALIZATION_LIBRARY_TYPE} */ public void setSerializationLibrary(final String enumSerializationLibrary) { try { @@ -449,35 +441,36 @@ public void processOpts() { } commonSupportingFiles(); - - switch (getLibrary()) { - case JVM_KTOR: - processJVMKtorLibrary(infrastructureFolder); - break; - case JVM_OKHTTP4: - processJVMOkHttpLibrary(infrastructureFolder); - break; - case JVM_VOLLEY: - processJVMVolleyLibrary(infrastructureFolder, requestFolder, authFolder); - break; - case JVM_RETROFIT2: - processJVMRetrofit2Library(infrastructureFolder); - break; - case JVM_SPRING_WEBCLIENT: - processJvmSpringWebClientLibrary(infrastructureFolder); - break; - case JVM_SPRING_RESTCLIENT: - processJvmSpringRestClientLibrary(infrastructureFolder); - break; - case MULTIPLATFORM: - processMultiplatformLibrary(infrastructureFolder); - break; - case JVM_VERTX: - processJVMVertXLibrary(infrastructureFolder); - break; - default: - break; - } + processJVMRetrofit2Library(infrastructureFolder); + +// switch (getLibrary()) { +// case JVM_KTOR: +// processJVMKtorLibrary(infrastructureFolder); +// break; +// case JVM_OKHTTP4: +// processJVMOkHttpLibrary(infrastructureFolder); +// break; +// case JVM_VOLLEY: +// processJVMVolleyLibrary(infrastructureFolder, requestFolder, authFolder); +// break; +// case JVM_RETROFIT2: +// processJVMRetrofit2Library(infrastructureFolder); +// break; +// case JVM_SPRING_WEBCLIENT: +// processJvmSpringWebClientLibrary(infrastructureFolder); +// break; +// case JVM_SPRING_RESTCLIENT: +// processJvmSpringRestClientLibrary(infrastructureFolder); +// break; +// case MULTIPLATFORM: +// processMultiplatformLibrary(infrastructureFolder); +// break; +// case JVM_VERTX: +// processJVMVertXLibrary(infrastructureFolder); +// break; +// default: +// break; +// } processDateLibrary(); processRequestDateConverter(); @@ -497,27 +490,27 @@ public void processOpts() { additionalProperties.put("isList", true); } - if (usesRetrofit2Library()) { - boolean hasOAuthMethods = ProcessUtils.hasOAuthMethods(openAPI); - - if (hasOAuthMethods) { - supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); - supportingFiles.add(new SupportingFile("auth/OAuthFlow.kt.mustache", authFolder, "OAuthFlow.kt")); - supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.kt.mustache", authFolder, "OAuthOkHttpClient.kt")); - } - - if (hasOAuthMethods || ProcessUtils.hasApiKeyMethods(openAPI)) { - supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); - } - - if (ProcessUtils.hasHttpBearerMethods(openAPI)) { - supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); - } - - if (ProcessUtils.hasHttpBasicMethods(openAPI)) { - supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); - } - } +// if (usesRetrofit2Library()) { +// boolean hasOAuthMethods = ProcessUtils.hasOAuthMethods(openAPI); +// +// if (hasOAuthMethods) { +// supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); +// supportingFiles.add(new SupportingFile("auth/OAuthFlow.kt.mustache", authFolder, "OAuthFlow.kt")); +// supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.kt.mustache", authFolder, "OAuthOkHttpClient.kt")); +// } +// +// if (hasOAuthMethods || ProcessUtils.hasApiKeyMethods(openAPI)) { +// supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); +// } +// +// if (ProcessUtils.hasHttpBearerMethods(openAPI)) { +// supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); +// } +// +// if (ProcessUtils.hasHttpBasicMethods(openAPI)) { +// supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); +// } +// } additionalProperties.put("sanitizeGeneric", (Mustache.Lambda) (fragment, writer) -> { String content = fragment.execute(); @@ -646,9 +639,9 @@ private void processJVMVolleyLibrary(String infrastructureFolder, String request supportingFiles.add(new SupportingFile("request/RequestFactory.mustache", requestFolder, "RequestFactory.kt")); supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); - if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.gson) { - throw new RuntimeException("This library currently only supports gson serialization. Try adding '--additional-properties serializationLibrary=gson' to your command."); - } +// if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.gson) { +// throw new RuntimeException("This library currently only supports gson serialization. Try adding '--additional-properties serializationLibrary=gson' to your command."); +// } addSupportingSerializerAdapters(infrastructureFolder); supportingFiles.remove(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); @@ -657,49 +650,49 @@ private void processJVMVolleyLibrary(String infrastructureFolder, String request private void addSupportingSerializerAdapters(final String infrastructureFolder) { supportingFiles.add(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); - switch (getSerializationLibrary()) { - case moshi: - if (enumUnknownDefaultCase) { - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/SerializerHelper.kt.mustache", infrastructureFolder, "SerializerHelper.kt")); - } - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); - addKotlinxDateTimeAdapters(infrastructureFolder); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); - break; - - case gson: - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); - addKotlinxDateTimeAdapters(infrastructureFolder); - break; - - case jackson: - break; - - case kotlinx_serialization: - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache", infrastructureFolder, "AtomicBooleanAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache", infrastructureFolder, "AtomicIntegerAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicLongAdapter.kt.mustache", infrastructureFolder, "AtomicLongAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URLAdapter.kt.mustache", infrastructureFolder, "URLAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/StringBuilderAdapter.kt.mustache", infrastructureFolder, "StringBuilderAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/proguard-rules.pro.mustache", "", "proguard-rules.pro")); - break; - } +// switch (getSerializationLibrary()) { +// case moshi: +// if (enumUnknownDefaultCase) { +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/SerializerHelper.kt.mustache", infrastructureFolder, "SerializerHelper.kt")); +// } +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); +// addKotlinxDateTimeAdapters(infrastructureFolder); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); +// break; +// +// case gson: +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); +// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); +// addKotlinxDateTimeAdapters(infrastructureFolder); +// break; +// +// case jackson: +// break; +// +// case kotlinx_serialization: + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache", infrastructureFolder, "AtomicBooleanAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache", infrastructureFolder, "AtomicIntegerAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicLongAdapter.kt.mustache", infrastructureFolder, "AtomicLongAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URLAdapter.kt.mustache", infrastructureFolder, "URLAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/StringBuilderAdapter.kt.mustache", infrastructureFolder, "StringBuilderAdapter.kt")); + supportingFiles.add(new SupportingFile("jvm-common/infrastructure/proguard-rules.pro.mustache", "", "proguard-rules.pro")); +// break; +// } } private void addKotlinxDateTimeAdapters(final String infrastructureFolder) { @@ -711,9 +704,9 @@ private void addKotlinxDateTimeAdapters(final String infrastructureFolder) { private void processJVMKtorLibrary(final String infrastructureFolder) { // in future kotlinx.serialization may be added - if (this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.gson && this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.jackson) { - this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.jackson; - } +// if (this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.gson && this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.jackson) { + this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.jackson; +// } additionalProperties.put(JVM, true); additionalProperties.put(JVM_KTOR, true); @@ -762,7 +755,7 @@ private void processJVMOkHttpLibrary(final String infrastructureFolder) { additionalProperties.put(JVM_OKHTTP4, true); } - supportedLibraries.put(JVM_OKHTTP, "A workaround to use the same template folder for both 'jvm-okhttp3' and 'jvm-okhttp4'."); +// supportedLibraries.put(JVM_OKHTTP, "A workaround to use the same template folder for both 'jvm-okhttp3' and 'jvm-okhttp4'."); setLibrary(JVM_OKHTTP); // jvm specific supporting files @@ -888,9 +881,11 @@ private void commonSupportingFiles() { @Override public ModelsMap postProcessModels(ModelsMap objs) { ModelsMap objects = super.postProcessModels(objs); + List allModels = objects.getModels(); - for (ModelMap mo : objects.getModels()) { + for (ModelMap mo : allModels) { CodegenModel cm = mo.getModel(); + if (getGenerateRoomModels() || getGenerateOneOfAnyOfWrappers()) { cm.vendorExtensions.put("x-has-data-class-body", true); } @@ -916,6 +911,73 @@ public ModelsMap postProcessModels(ModelsMap objs) { return objects; } + @Override + public Map postProcessAllModels(Map objs) { + + objs = super.postProcessAllModels(objs); + objs = super.updateAllModels(objs); + + Map> resourceRelationShips = new HashMap<>(Map.of()); + Map finalObjs = objs; + + // find all oneOf parents + objs.forEach((key, value) -> { + List m = value.getModels(); + for (ModelMap mo : m) { + CodegenModel cm = mo.getModel(); + + if (!cm.oneOf.isEmpty()) { + String name = cm.classname; + LOGGER.info("Model: " + cm.classname + " has oneOf: " + cm.oneOf.toString()); + for (String s : cm.oneOf) { + if (resourceRelationShips.containsKey(name)) { + // Key exists, so add the value to the existing list + resourceRelationShips.get(name).add(s); + } else { + // Key doesn't exist, so create a new list and add the value + List newList = new ArrayList<>(); + newList.add(s); + resourceRelationShips.put(name, newList); + } + } + } + } + }); + LOGGER.info("Resource Relationships: " + resourceRelationShips.toString()); + // find all oneOf children + List combinedList = new ArrayList<>(); + for (List values : resourceRelationShips.values()) { + combinedList.addAll(values); + } + objs.forEach((key, value) -> { + List m = value.getModels(); + for (ModelMap mo : m) { + CodegenModel cm = mo.getModel(); + LOGGER.info("Model: " + cm.classname); +// LOGGER.info("Model: " + cm.classname + resourceRelationShips.keySet().toString()); + + for (String className : combinedList) { + if (cm.classname == className) { + List matchingKeys = new ArrayList<>(); + LOGGER.info("Model: " + cm.classname + " is in the list "); + cm.vendorExtensions.put("x-has-oneof-relationship", true); + cm.allParents = new ArrayList(); + for (Map.Entry> entry : resourceRelationShips.entrySet()) { + List values = entry.getValue(); + if (values.contains(className)) { + matchingKeys.add(entry.getKey()); + } + } + cm.allParents.addAll(matchingKeys); + + } + } + } + + }); + return finalObjs; + } + private boolean usesRetrofit2Library() { return getLibrary() != null && getLibrary().contains(JVM_RETROFIT2); } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache index 9290173fd6b1..d1cfbc453038 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache @@ -80,7 +80,7 @@ import {{packageName}}.infrastructure.ITransformForStorage {{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} {{/allVars}} -){{/discriminator}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { +){{/discriminator}}{{#vendorExtensions.x-has-oneof-relationship}}: {{#allParents}}{{.}}ResourceType{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-relationship}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { {{/vendorExtensions.x-has-data-class-body}} {{#generateRoomModels}} companion object { } @@ -194,7 +194,7 @@ import {{packageName}}.infrastructure.ITransformForStorage companion object { var openapiFields = HashSet() var openapiRequiredFields = HashSet() - + init { {{#allVars}} {{#-first}} @@ -210,7 +210,7 @@ import {{packageName}}.infrastructure.ITransformForStorage openapiRequiredFields.add("{{baseName}}") {{/requiredVars}} } - + /** * Validates the JSON Element and throws an exception if issues found * @@ -227,7 +227,7 @@ import {{packageName}}.infrastructure.ITransformForStorage {{^hasChildren}} {{#requiredVars}} {{#-first}} - + // check to make sure all required properties/fields are present in the JSON string for (requiredField in openapiRequiredFields) { requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) { @@ -249,7 +249,7 @@ import {{packageName}}.infrastructure.ITransformForStorage if (!jsonObj.get("{{{baseName}}}").isJsonArray) { throw IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString())) } - + // validate the required field `{{{baseName}}}` (array) for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) { {{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i)) @@ -262,7 +262,7 @@ import {{packageName}}.infrastructure.ITransformForStorage require(jsonObj["{{{baseName}}}"].isJsonArray) { String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) } - + // validate the optional field `{{{baseName}}}` (array) for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) { {{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i)) From ab64e2d099b35ebd1fe54aa6aaa98f6c40b4a35a Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Mon, 14 Oct 2024 06:49:49 +0200 Subject: [PATCH 03/13] Generaate oneOf-kotlinx-serialization code --- .../languages/TidalKotlinClientCodegen.java | 97 ++++++++----------- .../resources/tidal-kotlin/Utils.kt.mustache | 19 ++++ .../tidal-kotlin/data_class.mustache | 2 +- .../infrastructure/Serializer.kt.mustache | 40 +++++--- .../tidal-kotlin/oneof_class.mustache | 19 +++- 5 files changed, 104 insertions(+), 73 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java index e64b16f3c1a2..b86812b33b53 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java @@ -32,6 +32,7 @@ import java.io.File; import java.util.*; +import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -97,7 +98,7 @@ public class TidalKotlinClientCodegen extends AbstractKotlinCodegen { @Setter protected boolean omitGradleWrapper = false; @Setter - protected boolean generateOneOfAnyOfWrappers = true; + protected boolean generateOneOfAnyOfWrappers = false; @Getter @Setter protected boolean failOnUnknownProperties = false; @@ -429,9 +430,10 @@ public void processOpts() { additionalProperties.put(this.serializationLibrary.name(), true); } - if (additionalProperties.containsKey(GENERATE_ONEOF_ANYOF_WRAPPERS)) { - setGenerateOneOfAnyOfWrappers(Boolean.parseBoolean(additionalProperties.get(GENERATE_ONEOF_ANYOF_WRAPPERS).toString())); - } +// if (additionalProperties.containsKey(GENERATE_ONEOF_ANYOF_WRAPPERS)) { +// setGenerateOneOfAnyOfWrappers(Boolean.parseBoolean(additionalProperties.get(GENERATE_ONEOF_ANYOF_WRAPPERS).toString())); +// } + setGenerateOneOfAnyOfWrappers(false); if (additionalProperties.containsKey(FAIL_ON_UNKNOWN_PROPERTIES)) { setFailOnUnknownProperties(Boolean.parseBoolean(additionalProperties.get(FAIL_ON_UNKNOWN_PROPERTIES).toString())); @@ -855,6 +857,8 @@ private void commonJvmMultiplatformSupportingFiles(String infrastructureFolder) } private void commonSupportingFiles() { + LOGGER.info("adding file"); + supportingFiles.add(new SupportingFile("Utils.kt.mustache", (sourceFolder + "." + modelPackage).replace(".", File.separator), "Utils.kt")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); if (getLibrary().equals(MULTIPLATFORM)) { supportingFiles.add(new SupportingFile("build.gradle.kts.mustache", "", "build.gradle.kts")); @@ -913,69 +917,52 @@ public ModelsMap postProcessModels(ModelsMap objs) { @Override public Map postProcessAllModels(Map objs) { - objs = super.postProcessAllModels(objs); objs = super.updateAllModels(objs); - Map> resourceRelationShips = new HashMap<>(Map.of()); - Map finalObjs = objs; + Map> resourceRelationShips = new HashMap<>(); + BiConsumer addToResourceRelationships = (key, value) -> { + resourceRelationShips.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + }; - // find all oneOf parents + // Find all oneOf parents objs.forEach((key, value) -> { - List m = value.getModels(); - for (ModelMap mo : m) { - CodegenModel cm = mo.getModel(); - - if (!cm.oneOf.isEmpty()) { - String name = cm.classname; - LOGGER.info("Model: " + cm.classname + " has oneOf: " + cm.oneOf.toString()); - for (String s : cm.oneOf) { - if (resourceRelationShips.containsKey(name)) { - // Key exists, so add the value to the existing list - resourceRelationShips.get(name).add(s); - } else { - // Key doesn't exist, so create a new list and add the value - List newList = new ArrayList<>(); - newList.add(s); - resourceRelationShips.put(name, newList); - } - } + List models = value.getModels(); + for (ModelMap modelMap : models) { + CodegenModel codegenModel = modelMap.getModel(); + + if (!codegenModel.oneOf.isEmpty()) { + String className = codegenModel.classname; + codegenModel.oneOf.forEach(oneOfClass -> addToResourceRelationships.accept(className, oneOfClass)); + } else { + // this field needs to be null to prevent mustache from getting triggered by an empty set + codegenModel.oneOf = null; } } }); - LOGGER.info("Resource Relationships: " + resourceRelationShips.toString()); - // find all oneOf children - List combinedList = new ArrayList<>(); - for (List values : resourceRelationShips.values()) { - combinedList.addAll(values); - } - objs.forEach((key, value) -> { - List m = value.getModels(); - for (ModelMap mo : m) { - CodegenModel cm = mo.getModel(); - LOGGER.info("Model: " + cm.classname); -// LOGGER.info("Model: " + cm.classname + resourceRelationShips.keySet().toString()); - - for (String className : combinedList) { - if (cm.classname == className) { - List matchingKeys = new ArrayList<>(); - LOGGER.info("Model: " + cm.classname + " is in the list "); - cm.vendorExtensions.put("x-has-oneof-relationship", true); - cm.allParents = new ArrayList(); - for (Map.Entry> entry : resourceRelationShips.entrySet()) { - List values = entry.getValue(); - if (values.contains(className)) { - matchingKeys.add(entry.getKey()); - } - } - cm.allParents.addAll(matchingKeys); - } + List combinedList = resourceRelationShips.values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + + // Find all oneOf children and assign relationships + objs.forEach((key, value) -> { + List models = value.getModels(); + for (ModelMap modelMap : models) { + CodegenModel codegenModel = modelMap.getModel(); + String className = codegenModel.classname; + + if (combinedList.contains(className)) { + codegenModel.vendorExtensions.put("x-has-oneof-parent", true); + codegenModel.allVars.removeIf(p -> p.name.equals("type")); + // Find all parents for the current class and add to allParents + codegenModel.allParents = resourceRelationShips.entrySet().stream() + .filter(entry -> entry.getValue().contains(className)) + .map(Map.Entry::getKey).collect(Collectors.toList()); } } - }); - return finalObjs; + return objs; } private boolean usesRetrofit2Library() { diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache new file mode 100644 index 000000000000..0f98f9f13116 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache @@ -0,0 +1,19 @@ +package {{modelPackage}} + +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic + +fun getOneOfSerializer() = SerializersModule { +{{#models}} +{{#model}} +{{#oneOf.size}} + polymorphic({{classname}}Resource::class) { +{{#oneOf}} + subclass({{.}}::class, {{.}}.serializer()) +{{/oneOf}} + } +{{/oneOf.size}} +{{/model}} +{{/models}} +} + diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache index d1cfbc453038..519ff0dd05ac 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache @@ -80,7 +80,7 @@ import {{packageName}}.infrastructure.ITransformForStorage {{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} {{/allVars}} -){{/discriminator}}{{#vendorExtensions.x-has-oneof-relationship}}: {{#allParents}}{{.}}ResourceType{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-relationship}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { +){{/discriminator}}{{#vendorExtensions.x-has-oneof-parent}}: {{#allParents}}{{.}}Resource{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-parent}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { {{/vendorExtensions.x-has-data-class-body}} {{#generateRoomModels}} companion object { } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache index 6b78b03b7af8..8ffdd2a76909 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache @@ -51,6 +51,17 @@ import org.threeten.bp.OffsetDateTime import java.util.UUID import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +{{#models}} + {{#model}} + {{#oneOf.size}} +import {{packageName}}.models.{{classname}}Resource +{{#oneOf}} +import {{packageName}}.models.{{.}} +{{/oneOf}} +{{/oneOf.size}} +{{/model}} +{{/models}} import java.net.URI import java.net.URL import java.util.concurrent.atomic.AtomicBoolean @@ -120,22 +131,23 @@ import java.util.concurrent.atomic.AtomicLong val kotlinSerializationAdapters: SerializersModule get() { return kotlinxSerializationAdapters } + val oneOfSerializers = SerializersModule { + {{#models}} + {{#model}} + {{#oneOf.size}} + polymorphic({{classname}}Resource::class) { + {{#oneOf}} + subclass({{.}}::class, {{.}}.serializer()) + {{/oneOf}} + } + {{/oneOf.size}} + {{/model}} + {{/models}} + } + @JvmStatic val kotlinxSerializationAdapters = SerializersModule { - contextual(BigDecimal::class, BigDecimalAdapter) - contextual(BigInteger::class, BigIntegerAdapter) - {{^kotlinx-datetime}} - contextual(LocalDate::class, LocalDateAdapter) - contextual(LocalDateTime::class, LocalDateTimeAdapter) - contextual(OffsetDateTime::class, OffsetDateTimeAdapter) - {{/kotlinx-datetime}} - contextual(UUID::class, UUIDAdapter) - contextual(AtomicInteger::class, AtomicIntegerAdapter) - contextual(AtomicLong::class, AtomicLongAdapter) - contextual(AtomicBoolean::class, AtomicBooleanAdapter) - contextual(URI::class, URIAdapter) - contextual(URL::class, URLAdapter) - contextual(StringBuilder::class, StringBuilderAdapter) + {{#models.oneOf.size}}include(oneOfSerializers){{/models.oneOf.size}} } @Deprecated("Use Serializer.kotlinxSerializationJson instead", replaceWith = ReplaceWith("Serializer.kotlinxSerializationJson")) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache index eebf9858773b..3c1afa2af7c5 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache @@ -30,6 +30,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} import kotlinx.serialization.SerialName import kotlinx.serialization.Contextual +import kotlinx.serialization.Polymorphic {{#enumUnknownDefaultCase}} import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -71,8 +72,19 @@ import java.io.IOException @Deprecated(message = "This schema is deprecated.") {{/isDeprecated}} {{>additionalModelTypeAnnotations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}(var actualInstance: Any? = null) { - +{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}( +{{^kotlinx_serialization}} + var actualInstance: Any? = null) +{{/kotlinx_serialization}} +{{#kotlinx_serialization}} + val resource: {{classname}}Resource? = null +) { +} +@Serializable +@Polymorphic +sealed interface {{classname}}Resource +{{/kotlinx_serialization}} +{{^kotlinx_serialization}} class CustomTypeAdapterFactory : TypeAdapterFactory { override fun create(gson: Gson, type: TypeToken): TypeAdapter? { if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { @@ -236,4 +248,5 @@ import java.io.IOException }.nullSafe() as TypeAdapter } } -} \ No newline at end of file +} +{{/kotlinx_serialization}} From 9161011177d5775ea510379f87954664c9c41d22 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Wed, 16 Oct 2024 10:04:57 +0200 Subject: [PATCH 04/13] Proper oneOf generation without Inner types --- .../codegen/languages/TidalKotlinClientCodegen.java | 1 + .../main/resources/tidal-kotlin/Utils.kt.mustache | 2 +- .../main/resources/tidal-kotlin/data_class.mustache | 12 +++++++++++- .../jvm-common/infrastructure/Serializer.kt.mustache | 2 +- test.sh | 10 ++++++++++ 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100755 test.sh diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java index b86812b33b53..5f312d9d112e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java @@ -934,6 +934,7 @@ public Map postProcessAllModels(Map objs) if (!codegenModel.oneOf.isEmpty()) { String className = codegenModel.classname; codegenModel.oneOf.forEach(oneOfClass -> addToResourceRelationships.accept(className, oneOfClass)); + codegenModel.vendorExtensions.put("x-is-oneof-parent", true); } else { // this field needs to be null to prevent mustache from getting triggered by an empty set codegenModel.oneOf = null; diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache index 0f98f9f13116..c5e9419da20c 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache @@ -7,7 +7,7 @@ fun getOneOfSerializer() = SerializersModule { {{#models}} {{#model}} {{#oneOf.size}} - polymorphic({{classname}}Resource::class) { + polymorphic({{classname}}::class) { {{#oneOf}} subclass({{.}}::class, {{.}}.serializer()) {{/oneOf}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache index 519ff0dd05ac..65a083dcd782 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache @@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} import kotlinx.serialization.SerialName import kotlinx.serialization.Contextual +import kotlinx.serialization.Polymorphic {{#enumUnknownDefaultCase}} import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -66,6 +67,14 @@ import {{packageName}}.infrastructure.ITransformForStorage * @param {{{name}}} {{{description}}} {{/allVars}} */ +{{#vendorExtensions.x-is-oneof-parent}} + {{#kotlinx_serialization}} +@Serializable +@Polymorphic +sealed interface {{classname}} + {{/kotlinx_serialization}} +{{/vendorExtensions.x-is-oneof-parent}} +{{^vendorExtensions.x-is-oneof-parent}} {{#parcelizeModels}} @Parcelize {{/parcelizeModels}} @@ -80,7 +89,7 @@ import {{packageName}}.infrastructure.ITransformForStorage {{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} {{/allVars}} -){{/discriminator}}{{#vendorExtensions.x-has-oneof-parent}}: {{#allParents}}{{.}}Resource{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-parent}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { +){{/discriminator}}{{#vendorExtensions.x-has-oneof-parent}}: {{#allParents}}{{.}}{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-parent}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { {{/vendorExtensions.x-has-data-class-body}} {{#generateRoomModels}} companion object { } @@ -361,3 +370,4 @@ import {{packageName}}.infrastructure.ITransformForStorage {{#vendorExtensions.x-has-data-class-body}} } {{/vendorExtensions.x-has-data-class-body}} +{{/vendorExtensions.x-is-oneof-parent}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache index 8ffdd2a76909..1c02bac73feb 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache @@ -55,7 +55,7 @@ import kotlinx.serialization.modules.polymorphic {{#models}} {{#model}} {{#oneOf.size}} -import {{packageName}}.models.{{classname}}Resource +import {{packageName}}.models.{{classname}} {{#oneOf}} import {{packageName}}.models.{{.}} {{/oneOf}} diff --git a/test.sh b/test.sh new file mode 100755 index 000000000000..f5095f07668a --- /dev/null +++ b/test.sh @@ -0,0 +1,10 @@ +#!/bin/zsh + +echo "Compiling" +mvn clean package -DskipTests + +echo "Go to module" +cd ~/work/tidal-sdk/tidal-sdk-android-private/userprofile + +echo "Run generate script" +./bin/generate.sh https://developer.tidal.com/apiref/api-specifications/api-public-user-jsonapi/tidal-user-v2-openapi-3.0.json From 1f06a655bca32dd9b7df21798d523496359f73fb Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Thu, 24 Oct 2024 07:08:33 +0200 Subject: [PATCH 05/13] Latest changes and details --- .../languages/TidalKotlinClientCodegen.java | 26 ++++++++++++++++--- .../tidal-kotlin/data_class.mustache | 8 ++++-- .../tidal-kotlin/data_class_opt_var.mustache | 1 + .../tidal-kotlin/data_class_req_var.mustache | 3 ++- test.sh | 2 +- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java index 5f312d9d112e..b578579b7921 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java @@ -433,7 +433,7 @@ public void processOpts() { // if (additionalProperties.containsKey(GENERATE_ONEOF_ANYOF_WRAPPERS)) { // setGenerateOneOfAnyOfWrappers(Boolean.parseBoolean(additionalProperties.get(GENERATE_ONEOF_ANYOF_WRAPPERS).toString())); // } - setGenerateOneOfAnyOfWrappers(false); + setGenerateOneOfAnyOfWrappers(true); if (additionalProperties.containsKey(FAIL_ON_UNKNOWN_PROPERTIES)) { setFailOnUnknownProperties(Boolean.parseBoolean(additionalProperties.get(FAIL_ON_UNKNOWN_PROPERTIES).toString())); @@ -886,7 +886,10 @@ private void commonSupportingFiles() { public ModelsMap postProcessModels(ModelsMap objs) { ModelsMap objects = super.postProcessModels(objs); List allModels = objects.getModels(); - + for (ModelMap mo : allModels) { + CodegenModel cm = mo.getModel(); + LOGGER.info("model: " + cm.classname); + } for (ModelMap mo : allModels) { CodegenModel cm = mo.getModel(); @@ -917,6 +920,7 @@ public ModelsMap postProcessModels(ModelsMap objs) { @Override public Map postProcessAllModels(Map objs) { + LOGGER.info("postProcessAllModels"); objs = super.postProcessAllModels(objs); objs = super.updateAllModels(objs); @@ -955,7 +959,23 @@ public Map postProcessAllModels(Map objs) if (combinedList.contains(className)) { codegenModel.vendorExtensions.put("x-has-oneof-parent", true); - codegenModel.allVars.removeIf(p -> p.name.equals("type")); + codegenModel.vendorExtensions.put("x-has-serializable-type", "hello"); + int index = codegenModel.classname.indexOf("Resource"); + if (index != -1) { + String serializableType = + Character.toLowerCase(codegenModel.classname.charAt(0)) + codegenModel.classname.substring(1, index); + codegenModel.vendorExtensions.put("x-has-serializable-type", serializableType); + } + + codegenModel.allVars.stream() + .filter(p -> Objects.equals(p.name, "type")) + .forEach(p -> { + LOGGER.info("Found type field in " + className); + p.vendorExtensions.put("x-is-transient", true); + LOGGER.info("VendorExtensions: " + p.vendorExtensions); + LOGGER.info("model VendorExtensions: " + codegenModel.vendorExtensions); + }); +// codegenModel.allVars.removeIf(p -> p.name.equals("type")); // Find all parents for the current class and add to allParents codegenModel.allParents = resourceRelationShips.entrySet().stream() .filter(entry -> entry.getValue().contains(className)) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache index 65a083dcd782..5f7c430d2661 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache @@ -32,6 +32,7 @@ import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable import kotlinx.serialization.SerialName import kotlinx.serialization.Contextual import kotlinx.serialization.Polymorphic +import kotlinx.serialization.Transient {{#enumUnknownDefaultCase}} import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -78,16 +79,19 @@ sealed interface {{classname}} {{#parcelizeModels}} @Parcelize {{/parcelizeModels}} + {{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} {{#isDeprecated}} @Deprecated(message = "This schema is deprecated.") {{/isDeprecated}} +{{#vendorExtensions.x-has-serializable-type}} +@SerialName(value = "{{vendorExtensions.x-has-serializable-type}}") +{{/vendorExtensions.x-has-serializable-type}} {{>additionalModelTypeAnnotations}} {{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}data class{{/discriminator}} {{classname}}{{^discriminator}} ( {{#allVars}} -{{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} - + {{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} {{/allVars}} ){{/discriminator}}{{#vendorExtensions.x-has-oneof-parent}}: {{#allParents}}{{.}}{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-parent}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { {{/vendorExtensions.x-has-data-class-body}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache index 1609fa8656e4..2c48810c1688 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache @@ -12,6 +12,7 @@ @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") {{/jackson}} {{#kotlinx_serialization}} + {{#vendorExtensions.x-is-transient}}@Transient{{/vendorExtensions.x-is-transient}} {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/kotlinx_serialization}} {{/multiplatform}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache index 3c9387d10151..9193f72b4401 100644 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache +++ b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache @@ -12,10 +12,11 @@ @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") {{/jackson}} {{#kotlinx_serialization}} + {{#vendorExtensions.x-is-transient}}@Transient{{/vendorExtensions.x-is-transient}} {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/kotlinx_serialization}} {{/multiplatform}} {{#deprecated}} @Deprecated(message = "This property is deprecated.") {{/deprecated}} - {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}} \ No newline at end of file + {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}{{#vendorExtensions.x-is-transient}} = ""{{/vendorExtensions.x-is-transient}} \ No newline at end of file diff --git a/test.sh b/test.sh index f5095f07668a..080232fd595f 100755 --- a/test.sh +++ b/test.sh @@ -7,4 +7,4 @@ echo "Go to module" cd ~/work/tidal-sdk/tidal-sdk-android-private/userprofile echo "Run generate script" -./bin/generate.sh https://developer.tidal.com/apiref/api-specifications/api-public-user-jsonapi/tidal-user-v2-openapi-3.0.json +./bin/generate.sh clean.json From 54f9a6ace2526a8b28e212c4f9befb2db3e154f9 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Thu, 24 Oct 2024 11:24:56 +0200 Subject: [PATCH 06/13] Add oneOf tags to mustache templates --- .../resources/kotlin-client/Utils.kt.mustache | 19 +++++++++++++++ .../kotlin-client/data_class.mustache | 23 ++++++++++++++----- .../kotlin-client/data_class_opt_var.mustache | 1 + .../kotlin-client/data_class_req_var.mustache | 3 ++- .../kotlin-client/oneof_class.mustache | 19 ++++++++++++--- 5 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/kotlin-client/Utils.kt.mustache diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/Utils.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/Utils.kt.mustache new file mode 100644 index 000000000000..c5e9419da20c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/kotlin-client/Utils.kt.mustache @@ -0,0 +1,19 @@ +package {{modelPackage}} + +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic + +fun getOneOfSerializer() = SerializersModule { +{{#models}} +{{#model}} +{{#oneOf.size}} + polymorphic({{classname}}::class) { +{{#oneOf}} + subclass({{.}}::class, {{.}}.serializer()) +{{/oneOf}} + } +{{/oneOf.size}} +{{/model}} +{{/models}} +} + diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache index 394af616a128..4291f0bc1d97 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache @@ -31,6 +31,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} import kotlinx.serialization.SerialName import kotlinx.serialization.Contextual +import kotlinx.serialization.Polymorphic +import kotlinx.serialization.Transient {{#enumUnknownDefaultCase}} import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -66,24 +68,32 @@ import {{packageName}}.infrastructure.ITransformForStorage * @param {{{name}}} {{{description}}} {{/allVars}} */ +{{#kotlinx_serialization}} +{{#vendorExtensions.x-is-oneof-parent}} +@Serializable +@Polymorphic +sealed interface {{classname}} +{{/vendorExtensions.x-is-oneof-parent}} +{{/kotlinx_serialization}} +{{^vendorExtensions.x-is-oneof-parent}} {{#parcelizeModels}} @Parcelize {{/parcelizeModels}} + {{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} {{#isDeprecated}} @Deprecated(message = "This schema is deprecated.") {{/isDeprecated}} +{{#vendorExtensions.x-has-serializable-type}} +@SerialName(value = "{{vendorExtensions.x-has-serializable-type}}") +{{/vendorExtensions.x-has-serializable-type}} {{>additionalModelTypeAnnotations}} -{{#vendorExtensions.x-class-extra-annotation}} -{{{vendorExtensions.x-class-extra-annotation}}} -{{/vendorExtensions.x-class-extra-annotation}} -{{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class{{/discriminator}} {{classname}}{{^discriminator}} ( +{{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}data class{{/discriminator}} {{classname}}{{^discriminator}} ( {{#allVars}} {{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} - {{/allVars}} -){{/discriminator}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { +){{/discriminator}}{{#vendorExtensions.x-has-oneof-parent}}: {{#allParents}}{{.}}{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-parent}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { {{/vendorExtensions.x-has-data-class-body}} {{#generateRoomModels}} companion object { } @@ -364,3 +374,4 @@ import {{packageName}}.infrastructure.ITransformForStorage {{#vendorExtensions.x-has-data-class-body}} } {{/vendorExtensions.x-has-data-class-body}} +{{/vendorExtensions.x-is-oneof-parent}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache index 897b18f9e599..851a0cc3fa16 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/data_class_opt_var.mustache @@ -12,6 +12,7 @@ @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") {{/jackson}} {{#kotlinx_serialization}} + {{#vendorExtensions.x-is-transient}}@Transient{{/vendorExtensions.x-is-transient}} {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/kotlinx_serialization}} {{/multiplatform}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/data_class_req_var.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/data_class_req_var.mustache index 811867dfc51f..01d156f1d6cc 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/data_class_req_var.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/data_class_req_var.mustache @@ -12,6 +12,7 @@ @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") {{/jackson}} {{#kotlinx_serialization}} + {{#vendorExtensions.x-is-transient}}@Transient{{/vendorExtensions.x-is-transient}} {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/kotlinx_serialization}} {{/multiplatform}} @@ -21,4 +22,4 @@ {{#deprecated}} @Deprecated(message = "This property is deprecated.") {{/deprecated}} - {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}} \ No newline at end of file + {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}{{#vendorExtensions.x-is-transient}} = ""{{/vendorExtensions.x-is-transient}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/oneof_class.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/oneof_class.mustache index eebf9858773b..3c1afa2af7c5 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-client/oneof_class.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-client/oneof_class.mustache @@ -30,6 +30,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} import kotlinx.serialization.SerialName import kotlinx.serialization.Contextual +import kotlinx.serialization.Polymorphic {{#enumUnknownDefaultCase}} import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializer @@ -71,8 +72,19 @@ import java.io.IOException @Deprecated(message = "This schema is deprecated.") {{/isDeprecated}} {{>additionalModelTypeAnnotations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}(var actualInstance: Any? = null) { - +{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}( +{{^kotlinx_serialization}} + var actualInstance: Any? = null) +{{/kotlinx_serialization}} +{{#kotlinx_serialization}} + val resource: {{classname}}Resource? = null +) { +} +@Serializable +@Polymorphic +sealed interface {{classname}}Resource +{{/kotlinx_serialization}} +{{^kotlinx_serialization}} class CustomTypeAdapterFactory : TypeAdapterFactory { override fun create(gson: Gson, type: TypeToken): TypeAdapter? { if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { @@ -236,4 +248,5 @@ import java.io.IOException }.nullSafe() as TypeAdapter } } -} \ No newline at end of file +} +{{/kotlinx_serialization}} From 519e54ee580d9278e169f2c46ba165522334149d Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Thu, 24 Oct 2024 11:32:57 +0200 Subject: [PATCH 07/13] Set up information to create sealed interfaces for oneOf in postProcessAllModels --- .../languages/KotlinClientCodegen.java | 72 +++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java index 69258bdf092b..1aeacd0b143d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java @@ -18,10 +18,8 @@ package org.openapitools.codegen.languages; import java.io.File; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -932,6 +930,72 @@ public ModelsMap postProcessModels(ModelsMap objs) { return objects; } + @Override + public Map postProcessAllModels(Map objs) { + objs = super.postProcessAllModels(objs); + objs = super.updateAllModels(objs); + + Map> resourceRelationShips = new HashMap<>(); + BiConsumer addToResourceRelationships = (key, value) -> { + resourceRelationShips.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + }; + + // Find all oneOf parents + objs.forEach((key, value) -> { + List models = value.getModels(); + for (ModelMap modelMap : models) { + CodegenModel codegenModel = modelMap.getModel(); + + if (!codegenModel.oneOf.isEmpty()) { + String className = codegenModel.classname; + codegenModel.oneOf.forEach(oneOfClass -> addToResourceRelationships.accept(className, oneOfClass)); + codegenModel.vendorExtensions.put("x-is-oneof-parent", true); + } else { + // this field needs to be null to prevent mustache from getting triggered by an empty set + codegenModel.oneOf = null; + } + } + }); + + List combinedList = resourceRelationShips.values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + + // Find all oneOf children and assign relationships + objs.forEach((key, value) -> { + List models = value.getModels(); + for (ModelMap modelMap : models) { + CodegenModel codegenModel = modelMap.getModel(); + String className = codegenModel.classname; + + if (combinedList.contains(className)) { + codegenModel.vendorExtensions.put("x-has-oneof-parent", true); + codegenModel.vendorExtensions.put("x-has-serializable-type", "hello"); + int index = codegenModel.classname.indexOf("Resource"); + if (index != -1) { + String serializableType = + Character.toLowerCase(codegenModel.classname.charAt(0)) + codegenModel.classname.substring(1, index); + codegenModel.vendorExtensions.put("x-has-serializable-type", serializableType); + } + + codegenModel.allVars.stream() + .filter(p -> Objects.equals(p.name, "type")) + .forEach(p -> { + LOGGER.info("Found type field in " + className); + p.vendorExtensions.put("x-is-transient", true); + LOGGER.info("VendorExtensions: " + p.vendorExtensions); + LOGGER.info("model VendorExtensions: " + codegenModel.vendorExtensions); + }); + // Find all parents for the current class and add to allParents + codegenModel.allParents = resourceRelationShips.entrySet().stream() + .filter(entry -> entry.getValue().contains(className)) + .map(Map.Entry::getKey).collect(Collectors.toList()); + } + } + }); + return objs; + } + private boolean usesRetrofit2Library() { return getLibrary() != null && getLibrary().contains(JVM_RETROFIT2); } From 1c0f09d7a4afe7308f837506619d5cf29da5b58a Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Thu, 24 Oct 2024 11:45:16 +0200 Subject: [PATCH 08/13] Simplify build workflow --- .github/workflows/openapi-generator.yaml | 282 +++++++++++------------ 1 file changed, 141 insertions(+), 141 deletions(-) diff --git a/.github/workflows/openapi-generator.yaml b/.github/workflows/openapi-generator.yaml index a48c4c4db470..0a38b7ea6156 100644 --- a/.github/workflows/openapi-generator.yaml +++ b/.github/workflows/openapi-generator.yaml @@ -2,9 +2,9 @@ name: OpenAPI Generator on: push: - branches: - - master - - '[5-9]+.[0-9]+.x' + # branches: + # - master + # - '[5-9]+.[0-9]+.x' pull_request: branches: - master @@ -47,147 +47,147 @@ jobs: path: modules/openapi-generator-cli/target/openapi-generator-cli.jar retention-days: 1 - test: - name: Unit tests - runs-on: ubuntu-latest - needs: - - build - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 - with: - java-version: 11 - distribution: 'temurin' - - name: Cache maven dependencies - uses: actions/cache@v4 - env: - cache-name: cache-maven-repository - with: - path: | - ~/.m2/repository - ~/.gradle - !~/.gradle/caches/*/plugin-resolution/ - !~/.m2/repository/org/openapitools/ - key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - - name: Run unit tests - run: ./mvnw clean --no-snapshot-updates --batch-mode --quiet --fail-at-end test -Dorg.slf4j.simpleLogger.defaultLogLevel=error - env: - GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - - name: Publish unit test reports - if: ${{ always() }} - uses: actions/upload-artifact@v4 - with: - name: surefire-test-results - path: '**/surefire-reports/TEST-*.xml' + # test: + # name: Unit tests + # runs-on: ubuntu-latest + # needs: + # - build + # steps: + # - uses: actions/checkout@v4 + # - name: Set up JDK 11 + # uses: actions/setup-java@v4 + # with: + # java-version: 11 + # distribution: 'temurin' + # - name: Cache maven dependencies + # uses: actions/cache@v4 + # env: + # cache-name: cache-maven-repository + # with: + # path: | + # ~/.m2/repository + # ~/.gradle + # !~/.gradle/caches/*/plugin-resolution/ + # !~/.m2/repository/org/openapitools/ + # key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }} + # restore-keys: | + # ${{ runner.os }}-build-${{ env.cache-name }}- + # ${{ runner.os }}-build- + # - name: Run unit tests + # run: ./mvnw clean --no-snapshot-updates --batch-mode --quiet --fail-at-end test -Dorg.slf4j.simpleLogger.defaultLogLevel=error + # env: + # GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} + # - name: Publish unit test reports + # if: ${{ always() }} + # uses: actions/upload-artifact@v4 + # with: + # name: surefire-test-results + # path: '**/surefire-reports/TEST-*.xml' - documentation: - name: Docs up-to-date - runs-on: ubuntu-latest - needs: - - build - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 - with: - java-version: 11 - distribution: 'temurin' - - name: Download openapi-generator-cli.jar artifact - uses: actions/download-artifact@v4 - with: - name: openapi-generator-cli.jar - path: modules/openapi-generator-cli/target - - name: Generate docs - run: | - bash bin/meta-codegen.sh - bash bin/utils/export_docs_generators.sh - bash bin/utils/copy-to-website.sh - bash bin/utils/export_generators_readme.sh - - name: Verify git status - run: | - if [[ "$(git status --porcelain)" != "" ]]; then - echo "UNCOMMITTED CHANGES ERROR" - echo "There are uncommitted changes in working tree after execution of 'bin/ensure-up-to-date'" - echo "Perform git diff" - git --no-pager diff - echo "Perform git status" - git status - echo -e "\nThis script runs in pull requests against the anticipated merge commit (as if the PR was merged now)." - echo "When you see unexpected files here, it likely means that there are newer commits in master that you need to " - echo -e "rebase or merge into your branch.\n" - echo "Please run 'bin/utils/ensure-up-to-date' locally and commit changes (UNCOMMITTED CHANGES ERROR)" - exit 1 - fi + # documentation: + # name: Docs up-to-date + # runs-on: ubuntu-latest + # needs: + # - build + # steps: + # - uses: actions/checkout@v4 + # - name: Set up JDK 11 + # uses: actions/setup-java@v4 + # with: + # java-version: 11 + # distribution: 'temurin' + # - name: Download openapi-generator-cli.jar artifact + # uses: actions/download-artifact@v4 + # with: + # name: openapi-generator-cli.jar + # path: modules/openapi-generator-cli/target + # - name: Generate docs + # run: | + # bash bin/meta-codegen.sh + # bash bin/utils/export_docs_generators.sh + # bash bin/utils/copy-to-website.sh + # bash bin/utils/export_generators_readme.sh + # - name: Verify git status + # run: | + # if [[ "$(git status --porcelain)" != "" ]]; then + # echo "UNCOMMITTED CHANGES ERROR" + # echo "There are uncommitted changes in working tree after execution of 'bin/ensure-up-to-date'" + # echo "Perform git diff" + # git --no-pager diff + # echo "Perform git status" + # git status + # echo -e "\nThis script runs in pull requests against the anticipated merge commit (as if the PR was merged now)." + # echo "When you see unexpected files here, it likely means that there are newer commits in master that you need to " + # echo -e "rebase or merge into your branch.\n" + # echo "Please run 'bin/utils/ensure-up-to-date' locally and commit changes (UNCOMMITTED CHANGES ERROR)" + # exit 1 + # fi - samples: - name: Samples up-to-date - needs: - - build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 - with: - java-version: 11 - distribution: 'temurin' - - name: Download openapi-generator-cli.jar artifact - uses: actions/download-artifact@v4 - with: - name: openapi-generator-cli.jar - path: modules/openapi-generator-cli/target - - name: Delete samples that are entirely generated - run: | - rm -rf samples/client/petstore/csharp/generichost/latest/Tags + # samples: + # name: Samples up-to-date + # needs: + # - build + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # - name: Set up JDK 11 + # uses: actions/setup-java@v4 + # with: + # java-version: 11 + # distribution: 'temurin' + # - name: Download openapi-generator-cli.jar artifact + # uses: actions/download-artifact@v4 + # with: + # name: openapi-generator-cli.jar + # path: modules/openapi-generator-cli/target + # - name: Delete samples that are entirely generated + # run: | + # rm -rf samples/client/petstore/csharp/generichost/latest/Tags - rm -rf samples/client/petstore/csharp/generichost/net8/AllOf - rm -rf samples/client/petstore/csharp/generichost/net8/AnyOf - rm -rf samples/client/petstore/csharp/generichost/net8/AnyOfNoCompare - rm -rf samples/client/petstore/csharp/generichost/net8/FormModels - rm -rf samples/client/petstore/csharp/generichost/net8/NullReferenceTypes - rm -rf samples/client/petstore/csharp/generichost/net8/OneOf - rm -rf samples/client/petstore/csharp/generichost/net8/Petstore - rm -rf samples/client/petstore/csharp/generichost/net8/SourceGeneration - rm -rf samples/client/petstore/csharp/generichost/net8/UseDateTimeForDate + # rm -rf samples/client/petstore/csharp/generichost/net8/AllOf + # rm -rf samples/client/petstore/csharp/generichost/net8/AnyOf + # rm -rf samples/client/petstore/csharp/generichost/net8/AnyOfNoCompare + # rm -rf samples/client/petstore/csharp/generichost/net8/FormModels + # rm -rf samples/client/petstore/csharp/generichost/net8/NullReferenceTypes + # rm -rf samples/client/petstore/csharp/generichost/net8/OneOf + # rm -rf samples/client/petstore/csharp/generichost/net8/Petstore + # rm -rf samples/client/petstore/csharp/generichost/net8/SourceGeneration + # rm -rf samples/client/petstore/csharp/generichost/net8/UseDateTimeForDate - rm -rf samples/client/petstore/csharp/generichost/standard2.0/Petstore + # rm -rf samples/client/petstore/csharp/generichost/standard2.0/Petstore - rm -rf samples/client/petstore/csharp/generichost/net4.8/AllOf - rm -rf samples/client/petstore/csharp/generichost/net4.8/AnyOf - rm -rf samples/client/petstore/csharp/generichost/net4.8/AnyOfNoCompare - rm -rf samples/client/petstore/csharp/generichost/net4.8/FormModels - rm -rf samples/client/petstore/csharp/generichost/net4.8/OneOf - rm -rf samples/client/petstore/csharp/generichost/net4.8/Petstore - rm -rf samples/client/petstore/csharp/generichost/net4.8/UseDateTimeForDate + # rm -rf samples/client/petstore/csharp/generichost/net4.8/AllOf + # rm -rf samples/client/petstore/csharp/generichost/net4.8/AnyOf + # rm -rf samples/client/petstore/csharp/generichost/net4.8/AnyOfNoCompare + # rm -rf samples/client/petstore/csharp/generichost/net4.8/FormModels + # rm -rf samples/client/petstore/csharp/generichost/net4.8/OneOf + # rm -rf samples/client/petstore/csharp/generichost/net4.8/Petstore + # rm -rf samples/client/petstore/csharp/generichost/net4.8/UseDateTimeForDate - rm -rf samples/client/petstore/csharp/generichost/net4.7/AllOf - rm -rf samples/client/petstore/csharp/generichost/net4.7/AnyOf - rm -rf samples/client/petstore/csharp/generichost/net4.7/AnyOfNoCompare - rm -rf samples/client/petstore/csharp/generichost/net4.7/FormModels - rm -rf samples/client/petstore/csharp/generichost/net4.7/OneOf - rm -rf samples/client/petstore/csharp/generichost/net4.7/Petstore - rm -rf samples/client/petstore/csharp/generichost/net4.7/UseDateTimeForDate - - name: Generate samples - run: | - bash bin/generate-samples.sh - # when a sample is deleted, you have to generate it twice for all files to get created - bash bin/generate-samples.sh - - name: Verify git status - run: | - if [[ "$(git status --porcelain)" != "" ]]; then - echo "UNCOMMITTED CHANGES ERROR" - echo "There are uncommitted changes in working tree after execution of 'bin/generate-samples.sh'" - echo "Perform git diff" - git --no-pager diff - echo "Perform git status" - git status - echo -e "\nThis script runs in pull requests against the anticipated merge commit (as if the PR was merged now)." - echo "When you see unexpected files here, it likely means that there are newer commits in master that you need to " - echo -e "rebase or merge into your branch.\n" - echo "Please run 'bin/generate-samples.sh' locally and commit changes (UNCOMMITTED CHANGES ERROR)" - exit 1 - fi + # rm -rf samples/client/petstore/csharp/generichost/net4.7/AllOf + # rm -rf samples/client/petstore/csharp/generichost/net4.7/AnyOf + # rm -rf samples/client/petstore/csharp/generichost/net4.7/AnyOfNoCompare + # rm -rf samples/client/petstore/csharp/generichost/net4.7/FormModels + # rm -rf samples/client/petstore/csharp/generichost/net4.7/OneOf + # rm -rf samples/client/petstore/csharp/generichost/net4.7/Petstore + # rm -rf samples/client/petstore/csharp/generichost/net4.7/UseDateTimeForDate + # - name: Generate samples + # run: | + # bash bin/generate-samples.sh + # # when a sample is deleted, you have to generate it twice for all files to get created + # bash bin/generate-samples.sh + # - name: Verify git status + # run: | + # if [[ "$(git status --porcelain)" != "" ]]; then + # echo "UNCOMMITTED CHANGES ERROR" + # echo "There are uncommitted changes in working tree after execution of 'bin/generate-samples.sh'" + # echo "Perform git diff" + # git --no-pager diff + # echo "Perform git status" + # git status + # echo -e "\nThis script runs in pull requests against the anticipated merge commit (as if the PR was merged now)." + # echo "When you see unexpected files here, it likely means that there are newer commits in master that you need to " + # echo -e "rebase or merge into your branch.\n" + # echo "Please run 'bin/generate-samples.sh' locally and commit changes (UNCOMMITTED CHANGES ERROR)" + # exit 1 + # fi From 98bb7ab6db2cf17ff6481fe568fc2af6ea94a709 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Thu, 24 Oct 2024 13:08:36 +0200 Subject: [PATCH 09/13] Change artifact retention days to 90 --- .github/workflows/openapi-generator.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/openapi-generator.yaml b/.github/workflows/openapi-generator.yaml index 0a38b7ea6156..3ab310a542a4 100644 --- a/.github/workflows/openapi-generator.yaml +++ b/.github/workflows/openapi-generator.yaml @@ -45,7 +45,7 @@ jobs: with: name: openapi-generator-cli.jar path: modules/openapi-generator-cli/target/openapi-generator-cli.jar - retention-days: 1 + retention-days: 90 # test: # name: Unit tests From 9bcc9f8626e48ae0ecdddd9257bff5e01140bd59 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Fri, 25 Oct 2024 06:10:53 +0200 Subject: [PATCH 10/13] Try releasing --- .github/workflows/openapi-generator.yaml | 35 ++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/workflows/openapi-generator.yaml b/.github/workflows/openapi-generator.yaml index 3ab310a542a4..a91c44b58699 100644 --- a/.github/workflows/openapi-generator.yaml +++ b/.github/workflows/openapi-generator.yaml @@ -1,5 +1,8 @@ name: OpenAPI Generator +permissions: + contents: write + on: push: # branches: @@ -40,13 +43,35 @@ jobs: env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - run: ls -la modules/openapi-generator-cli/target - - name: Upload openapi-generator-cli.jar artifact - uses: actions/upload-artifact@v4 + + - name: Generate tag string + id: generate_snapshot_string + run: | + SNAPSHOT_STRING="tidal-SNAPSHOT-$(date +%Y-%m-%d)" + echo "Generated string: $SNAPSHOT_STRING" + + # Set the output for this step + echo "::set-output name=snapshot_string::$SNAPSHOT_STRING" + + - name: Generate Release Draft + id: generate-release + uses: ncipollo/release-action@v1 with: - name: openapi-generator-cli.jar - path: modules/openapi-generator-cli/target/openapi-generator-cli.jar - retention-days: 90 + tag: ${{ steps.generate_snapshot_string.outputs.snapshot_string }} + allowUpdates: true + draft: true + body: "This is a release draft for ${{ github.ref }}" + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: modules/openapi-generator-cli/target/openapi-generator-cli.jar + asset_name: openapi-generator-cli.jar + tag: ${{ steps.generate_snapshot_string.outputs.snapshot_string }} + overwrite: true + target_commit: "" + body: "This is the latest snapshot release of our openapi-generator fork" # test: # name: Unit tests # runs-on: ubuntu-latest From 852b010234fc4bc61ada68330f499a1eb446c911 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Fri, 25 Oct 2024 13:20:01 +0200 Subject: [PATCH 11/13] Remove tidal-kotlin It's not needed anymore --- .../languages/TidalKotlinClientCodegen.java | 1107 ----------------- .../resources/tidal-kotlin/README.mustache | 113 -- .../resources/tidal-kotlin/Utils.kt.mustache | 19 - .../additionalModelTypeAnnotations.mustache | 2 - .../tidal-kotlin/anyof_class.mustache | 241 ---- .../resources/tidal-kotlin/api_doc.mustache | 92 -- .../resources/tidal-kotlin/api_test.mustache | 33 - .../tidal-kotlin/build.gradle.mustache | 216 ---- .../resources/tidal-kotlin/class_doc.mustache | 15 - .../tidal-kotlin/data_class.mustache | 377 ------ .../tidal-kotlin/data_class_opt_var.mustache | 22 - .../tidal-kotlin/data_class_req_var.mustache | 22 - .../tidal-kotlin/enum_class.mustache | 113 -- .../resources/tidal-kotlin/enum_doc.mustache | 7 - .../resources/tidal-kotlin/gradle-wrapper.jar | Bin 43462 -> 0 bytes .../gradle-wrapper.properties.mustache | 12 - .../tidal-kotlin/gradlew.bat.mustache | 89 -- .../resources/tidal-kotlin/gradlew.mustache | 249 ---- .../ApiAbstractions.kt.mustache | 23 - .../infrastructure/PartConfig.kt.mustache | 11 - .../infrastructure/RequestConfig.kt.mustache | 19 - .../infrastructure/RequestMethod.kt.mustache | 8 - .../tidal-kotlin/interface_opt_var.mustache | 18 - .../tidal-kotlin/interface_req_var.mustache | 18 - .../AtomicBooleanAdapter.kt.mustache | 21 - .../AtomicIntegerAdapter.kt.mustache | 21 - .../AtomicLongAdapter.kt.mustache | 21 - .../BigDecimalAdapter.kt.mustache | 38 - .../BigIntegerAdapter.kt.mustache | 43 - .../ByteArrayAdapter.kt.mustache | 50 - .../infrastructure/InstantAdapter.kt.mustache | 56 - .../LocalDateAdapter.kt.mustache | 103 -- .../LocalDateTimeAdapter.kt.mustache | 103 -- .../LocalTimeAdapter.kt.mustache | 56 - .../OffsetDateTimeAdapter.kt.mustache | 86 -- .../infrastructure/Serializer.kt.mustache | 168 --- .../SerializerHelper.kt.mustache | 50 - .../StringBuilderAdapter.kt.mustache | 20 - .../infrastructure/URIAdapter.kt.mustache | 38 - .../infrastructure/URLAdapter.kt.mustache | 21 - .../infrastructure/UUIDAdapter.kt.mustache | 40 - .../proguard-rules.pro.mustache | 11 - .../libraries/jvm-ktor/api.mustache | 152 --- .../jvm-ktor/auth/ApiKeyAuth.kt.mustache | 16 - .../jvm-ktor/auth/Authentication.kt.mustache | 13 - .../jvm-ktor/auth/HttpBasicAuth.kt.mustache | 17 - .../jvm-ktor/auth/HttpBearerAuth.kt.mustache | 14 - .../libraries/jvm-ktor/auth/OAuth.kt.mustache | 10 - .../infrastructure/ApiClient.kt.mustache | 229 ---- .../infrastructure/HttpResponse.kt.mustache | 51 - .../libraries/jvm-okhttp/api.mustache | 248 ---- .../infrastructure/ApiClient.kt.mustache | 448 ------- .../infrastructure/ApiResponse.kt.mustache | 43 - .../infrastructure/Errors.kt.mustache | 18 - .../ResponseExtensions.kt.mustache | 24 - .../libraries/jvm-retrofit2/api.mustache | 161 --- .../libraries/jvm-retrofit2/api_doc.mustache | 88 -- .../jvm-retrofit2/auth/ApiKeyAuth.kt.mustache | 50 - .../auth/HttpBasicAuth.kt.mustache | 33 - .../auth/HttpBearerAuth.kt.mustache | 39 - .../jvm-retrofit2/auth/OAuth.kt.mustache | 151 --- .../jvm-retrofit2/auth/OAuthFlow.kt.mustache | 5 - .../auth/OAuthOkHttpClient.kt.mustache | 61 - .../jvm-retrofit2/bodyParams.mustache | 1 - .../jvm-retrofit2/explodedQueryParam.mustache | 1 - .../jvm-retrofit2/formParams.mustache | 1 - .../jvm-retrofit2/headerParams.mustache | 1 - .../infrastructure/ApiClient.kt.mustache | 412 ------ .../CollectionFormats.kt.mustache | 56 - .../infrastructure/ResponseExt.kt.mustache | 35 - .../jvm-retrofit2/paramJavadoc.mustache | 5 - .../jvm-retrofit2/pathParams.mustache | 1 - .../jvm-retrofit2/queryParam.mustache | 1 - .../jvm-retrofit2/queryParams.mustache | 1 - .../jvm-spring-restclient/api.mustache | 147 --- .../infrastructure/ApiClient.kt.mustache | 68 - .../jvm-spring-webclient/api.mustache | 149 --- .../infrastructure/ApiClient.kt.mustache | 69 - .../libraries/jvm-vertx/api.mustache | 232 ---- .../infrastructure/ApiClient.kt.mustache | 110 -- .../infrastructure/ApiResponse.kt.mustache | 43 - .../infrastructure/Errors.kt.mustache | 18 - .../libraries/jvm-volley/README.mustache | 230 ---- .../libraries/jvm-volley/api.mustache | 116 -- .../libraries/jvm-volley/api_doc.mustache | 83 -- .../jvm-volley/auth/apikeyauth.mustache | 16 - .../jvm-volley/auth/authentication.mustache | 15 - .../jvm-volley/auth/httpbasicauth.mustache | 3 - .../libraries/jvm-volley/auth/oauth.mustache | 5 - .../libraries/jvm-volley/bodyParams.mustache | 1 - .../libraries/jvm-volley/build.mustache | 111 -- .../libraries/jvm-volley/formParams.mustache | 1 - .../jvm-volley/gradle.properties.mustache | 4 - .../jvm-volley/headerParams.mustache | 1 - .../CollectionFormats.kt.mustache | 56 - .../ITransformForStorage.mustache | 9 - .../libraries/jvm-volley/manifest.mustache | 5 - .../libraries/jvm-volley/pathParams.mustache | 1 - .../libraries/jvm-volley/queryParams.mustache | 1 - .../jvm-volley/request/GsonRequest.mustache | 119 -- .../request/IRequestFactory.mustache | 64 - .../request/RequestFactory.mustache | 69 - .../libraries/multiplatform/api.mustache | 135 -- .../multiplatform/auth/ApiKeyAuth.kt.mustache | 16 - .../auth/Authentication.kt.mustache | 13 - .../auth/HttpBasicAuth.kt.mustache | 17 - .../auth/HttpBearerAuth.kt.mustache | 14 - .../multiplatform/auth/OAuth.kt.mustache | 10 - .../multiplatform/build.gradle.kts.mustache | 103 -- .../commonTest/Coroutine.kt.mustache | 13 - .../infrastructure/ApiClient.kt.mustache | 199 --- .../Base64ByteArray.kt.mustache | 29 - .../infrastructure/Bytes.kt.mustache | 101 -- .../infrastructure/HttpResponse.kt.mustache | 51 - .../infrastructure/OctetByteArray.kt.mustache | 29 - .../iosTest/Coroutine.kt.mustache | 8 - .../jsTest/Coroutine.kt.mustache | 7 - .../jvmTest/Coroutine.kt.mustache | 8 - .../libraries/multiplatform/pom.mustache | 64 - .../serial_wrapper_request_list.mustache | 9 - .../serial_wrapper_request_map.mustache | 9 - .../serial_wrapper_response_list.mustache | 9 - .../serial_wrapper_response_map.mustache | 9 - .../settings.gradle.kts.mustache | 1 - .../tidal-kotlin/licenseInfo.mustache | 14 - .../resources/tidal-kotlin/model.mustache | 16 - .../tidal-kotlin/modelMutable.mustache | 1 - .../resources/tidal-kotlin/model_doc.mustache | 3 - .../tidal-kotlin/model_room.mustache | 38 - .../tidal-kotlin/model_room_init_var.mustache | 1 - .../tidal-kotlin/model_test.mustache | 29 - .../tidal-kotlin/oneof_class.mustache | 252 ---- .../tidal-kotlin/param_default_value.mustache | 1 - .../tidal-kotlin/settings.gradle.mustache | 1 - .../tidal-kotlin/typeInfoAnnotation.mustache | 6 - 135 files changed, 9120 deletions(-) delete mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.jar delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache delete mode 100755 modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache delete mode 100644 modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java deleted file mode 100644 index b578579b7921..000000000000 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TidalKotlinClientCodegen.java +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) - * Copyright 2018 SmartBear Software - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.openapitools.codegen.languages; - -import com.samskivert.mustache.Mustache; -import lombok.Getter; -import lombok.Setter; -import org.apache.commons.lang3.StringUtils; -import org.openapitools.codegen.*; -import org.openapitools.codegen.meta.features.*; -import org.openapitools.codegen.model.ModelMap; -import org.openapitools.codegen.model.ModelsMap; -import org.openapitools.codegen.model.OperationMap; -import org.openapitools.codegen.model.OperationsMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.util.Collections.sort; - -public class TidalKotlinClientCodegen extends AbstractKotlinCodegen { - - private final Logger LOGGER = LoggerFactory.getLogger(TidalKotlinClientCodegen.class); - - protected static final String JVM = "jvm"; - protected static final String JVM_KTOR = "jvm-ktor"; - protected static final String JVM_OKHTTP = "jvm-okhttp"; - protected static final String JVM_OKHTTP4 = "jvm-okhttp4"; - protected static final String JVM_RETROFIT2 = "jvm-retrofit2"; - protected static final String MULTIPLATFORM = "multiplatform"; - protected static final String JVM_VOLLEY = "jvm-volley"; - protected static final String JVM_VERTX = "jvm-vertx"; - protected static final String JVM_SPRING = "jvm-spring"; - protected static final String JVM_SPRING_WEBCLIENT = "jvm-spring-webclient"; - protected static final String JVM_SPRING_RESTCLIENT = "jvm-spring-restclient"; - - public static final String USE_RX_JAVA3 = "useRxJava3"; - public static final String USE_COROUTINES = "useCoroutines"; - public static final String DO_NOT_USE_RX_AND_COROUTINES = "doNotUseRxAndCoroutines"; - public static final String GENERATE_ROOM_MODELS = "generateRoomModels"; - public static final String ROOM_MODEL_PACKAGE = "roomModelPackage"; - public static final String OMIT_GRADLE_PLUGIN_VERSIONS = "omitGradlePluginVersions"; - public static final String OMIT_GRADLE_WRAPPER = "omitGradleWrapper"; - public static final String USE_SETTINGS_GRADLE = "useSettingsGradle"; - public static final String IDEA = "idea"; - public static final String USE_SPRING_BOOT3 = "useSpringBoot3"; - - public static final String DATE_LIBRARY = "dateLibrary"; - public static final String REQUEST_DATE_CONVERTER = "requestDateConverter"; - public static final String COLLECTION_TYPE = "collectionType"; - public static final String FAIL_ON_UNKNOWN_PROPERTIES = "failOnUnknownProperties"; - - public static final String MOSHI_CODE_GEN = "moshiCodeGen"; - - public static final String NULLABLE_RETURN_TYPE = "nullableReturnType"; - - public static final String SUPPORT_ANDROID_API_LEVEL_25_AND_BELLOW = "supportAndroidApiLevel25AndBelow"; - - public static final String GENERATE_ONEOF_ANYOF_WRAPPERS = "generateOneOfAnyOfWrappers"; - - protected static final String VENDOR_EXTENSION_BASE_NAME_LITERAL = "x-base-name-literal"; - - @Setter - protected String dateLibrary = DateLibrary.JAVA8.value; - @Setter - protected String requestDateConverter = RequestDateConverter.TO_JSON.value; - @Setter - protected String collectionType = CollectionType.LIST.value; - protected boolean useRxJava3 = false; - protected boolean useCoroutines = false; - // backwards compatibility for openapi configs that specify neither rx1 nor rx2 - // (mustache does not allow for boolean operators so we need this extra field) - protected boolean doNotUseRxAndCoroutines = true; - protected boolean generateRoomModels = false; - @Setter - protected String roomModelPackage = ""; - @Setter - protected boolean omitGradleWrapper = false; - @Setter - protected boolean generateOneOfAnyOfWrappers = false; - @Getter - @Setter - protected boolean failOnUnknownProperties = false; - - protected String authFolder; - - @Getter - protected SERIALIZATION_LIBRARY_TYPE serializationLibrary = SERIALIZATION_LIBRARY_TYPE.kotlinx_serialization; - public static final String SERIALIZATION_LIBRARY_DESC = "What serialization library to use: 'moshi' (default), or 'gson' or 'jackson' or 'kotlinx_serialization'"; - - public enum SERIALIZATION_LIBRARY_TYPE {moshi, jackson, kotlinx_serialization} - - public enum DateLibrary { - STRING("string"), - THREETENBP("threetenbp"), - THREETENBP_LOCALDATETIME("threetenbp-localdatetime"), - JAVA8("java8"), - JAVA8_LOCALDATETIME("java8-localdatetime"), - KOTLINX_DATETIME("kotlinx-datetime"); - - public final String value; - - DateLibrary(String value) { - this.value = value; - } - } - - public enum RequestDateConverter { - TO_STRING("toString"), - TO_JSON("toJson"); - - public final String value; - - RequestDateConverter(String value) { - this.value = value; - } - } - - public enum CollectionType { - ARRAY("array"), - LIST("list"); - - public final String value; - - CollectionType(String value) { - this.value = value; - } - } - - /** - * Constructs an instance of `TidalKotlinClientCodegen`. - */ - public TidalKotlinClientCodegen() { - super(); - - /* - * OAuth flows supported _only_ by client explicitly setting bearer token. The "flows" are not supported. - */ - modifyFeatureSet(features -> features - .includeDocumentationFeatures(DocumentationFeature.Readme) - .excludeWireFormatFeatures(WireFormatFeature.XML, WireFormatFeature.PROTOBUF) - .securityFeatures(EnumSet.of( - SecurityFeature.BasicAuth, - SecurityFeature.ApiKey, - SecurityFeature.BearerToken, - SecurityFeature.OAuth2_AuthorizationCode,//retrofit only - SecurityFeature.OAuth2_Implicit)) - .excludeGlobalFeatures( - GlobalFeature.XMLStructureDefinitions, - GlobalFeature.Callbacks, - GlobalFeature.LinkObjects, - GlobalFeature.ParameterStyling - ) - .excludeSchemaSupportFeatures( -// SchemaSupportFeature.Polymorphism - ) - .excludeParameterFeatures( - ParameterFeature.Cookie - ) - .includeClientModificationFeatures(ClientModificationFeature.BasePath) - ); - - artifactId = "tidal-kotlin-client"; - packageName = "org.openapitools.client"; - - // cliOptions default redefinition need to be updated - updateOption(CodegenConstants.ARTIFACT_ID, this.artifactId); - updateOption(CodegenConstants.PACKAGE_NAME, this.packageName); - - outputFolder = "generated-code" + File.separator + "tidal-kotlin"; - modelTemplateFiles.put("model.mustache", ".kt"); -// if (generateRoomModels) { -// modelTemplateFiles.put("model_room.mustache", ".kt"); -// } - apiTemplateFiles.put("api.mustache", ".kt"); - apiTestTemplateFiles.put("api_test.mustache", ".kt"); - modelTestTemplateFiles.put("model_test.mustache", ".kt"); - modelDocTemplateFiles.put("model_doc.mustache", ".md"); - apiDocTemplateFiles.put("api_doc.mustache", ".md"); - embeddedTemplateDir = templateDir = "tidal-kotlin"; - apiPackage = packageName + ".apis"; - modelPackage = packageName + ".models"; - - CliOption dateLibrary = new CliOption(DATE_LIBRARY, "Option. Date library to use"); - Map dateOptions = new HashMap<>(); - dateOptions.put(DateLibrary.THREETENBP.value, "Threetenbp - Backport of JSR310 (jvm only, preferred for jdk < 1.8)"); - dateOptions.put(DateLibrary.THREETENBP_LOCALDATETIME.value, "Threetenbp - Backport of JSR310 (jvm only, for legacy app only)"); - dateOptions.put(DateLibrary.STRING.value, "String"); - dateOptions.put(DateLibrary.JAVA8.value, "Java 8 native JSR310 (jvm only, preferred for jdk 1.8+)"); - dateOptions.put(DateLibrary.JAVA8_LOCALDATETIME.value, "Java 8 native JSR310 (jvm only, for legacy app only)"); - dateOptions.put(DateLibrary.KOTLINX_DATETIME.value, "kotlinx-datetime (preferred for multiplatform)"); - dateLibrary.setEnum(dateOptions); - dateLibrary.setDefault(this.dateLibrary); - cliOptions.add(dateLibrary); - - CliOption collectionType = new CliOption(COLLECTION_TYPE, "Option. Collection type to use"); - Map collectionOptions = new HashMap<>(); - collectionOptions.put(CollectionType.ARRAY.value, "kotlin.Array"); - collectionOptions.put(CollectionType.LIST.value, "kotlin.collections.List"); - collectionType.setEnum(collectionOptions); - collectionType.setDefault(this.collectionType); - cliOptions.add(collectionType); - -// supportedLibraries.put(JVM_KTOR, "Platform: Java Virtual Machine. HTTP client: Ktor 1.6.7. JSON processing: Gson, Jackson (default)."); -// supportedLibraries.put(JVM_OKHTTP4, "[DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0."); -// supportedLibraries.put(JVM_SPRING_WEBCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 5 (or 6 with useSpringBoot3 enabled) WebClient. JSON processing: Jackson."); -// supportedLibraries.put(JVM_SPRING_RESTCLIENT, "Platform: Java Virtual Machine. HTTP: Spring 6 RestClient. JSON processing: Jackson."); - supportedLibraries.put(JVM_RETROFIT2, "Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2."); - supportedLibraries.put(MULTIPLATFORM, "Platform: Kotlin multiplatform. HTTP client: Ktor 1.6.7. JSON processing: Kotlinx Serialization: 1.2.1."); -// supportedLibraries.put(JVM_VOLLEY, "Platform: JVM for Android. HTTP client: Volley 1.2.1. JSON processing: gson 2.8.9"); -// supportedLibraries.put(JVM_VERTX, "Platform: Java Virtual Machine. HTTP client: Vert.x Web Client. JSON processing: Moshi, Gson or Jackson."); - - CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "Library template (sub-template) to use"); - libraryOption.setEnum(supportedLibraries); - libraryOption.setDefault(JVM_OKHTTP4); - cliOptions.add(libraryOption); - setLibrary(JVM_RETROFIT2); - - CliOption requestDateConverter = new CliOption(REQUEST_DATE_CONVERTER, "JVM-Option. Defines in how to handle date-time objects that are used for a request (as query or parameter)"); - Map requestDateConverterOptions = new HashMap<>(); - requestDateConverterOptions.put(RequestDateConverter.TO_JSON.value, "[DEFAULT] Date formatter option using a json converter."); - requestDateConverterOptions.put(RequestDateConverter.TO_STRING.value, "Use the 'toString'-method of the date-time object to retrieve the related string representation."); - requestDateConverter.setEnum(requestDateConverterOptions); - requestDateConverter.setDefault(this.requestDateConverter); - cliOptions.add(requestDateConverter); - -// cliOptions.add(CliOption.newBoolean(USE_RX_JAVA3, "Whether to use the RxJava3 adapter with the retrofit2 library.")); - cliOptions.add(CliOption.newBoolean(USE_COROUTINES, "Whether to use the Coroutines adapter with the retrofit2 library.")); -// cliOptions.add(CliOption.newBoolean(USE_SPRING_BOOT3, "Whether to use the Spring Boot 3 with the jvm-spring-webclient library.")); - cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_PLUGIN_VERSIONS, "Whether to declare Gradle plugin versions in build files.")); -// cliOptions.add(CliOption.newBoolean(OMIT_GRADLE_WRAPPER, "Whether to omit Gradle wrapper for creating a sub project.")); -// cliOptions.add(CliOption.newBoolean(USE_SETTINGS_GRADLE, "Whether the project uses settings.gradle.")); -// cliOptions.add(CliOption.newBoolean(IDEA, "Add IntellJ Idea plugin and mark Kotlin main and test folders as source folders.")); - -// cliOptions.add(CliOption.newBoolean(MOSHI_CODE_GEN, "Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info.")); -// cliOptions.add(CliOption.newBoolean(FAIL_ON_UNKNOWN_PROPERTIES, "Fail Jackson de-serialization on unknown properties", false)); - - cliOptions.add(CliOption.newBoolean(NULLABLE_RETURN_TYPE, "Nullable return type")); - -// cliOptions.add(CliOption.newBoolean(GENERATE_ROOM_MODELS, "Generate Android Room database models in addition to API models (JVM Volley library only)", false)); - - cliOptions.add(CliOption.newBoolean(SUPPORT_ANDROID_API_LEVEL_25_AND_BELLOW, "[WARNING] This flag will generate code that has a known security vulnerability. It uses `kotlin.io.createTempFile` instead of `java.nio.file.Files.createTempFile` in order to support Android API level 25 and bellow. For more info, please check the following links https://github.com/OpenAPITools/openapi-generator/security/advisories/GHSA-23x4-m842-fmwf, https://github.com/OpenAPITools/openapi-generator/pull/9284")); - - cliOptions.add(CliOption.newBoolean(GENERATE_ONEOF_ANYOF_WRAPPERS, "Generate oneOf, anyOf schemas as wrappers.")); - - CliOption serializationLibraryOpt = new CliOption(CodegenConstants.SERIALIZATION_LIBRARY, SERIALIZATION_LIBRARY_DESC); - cliOptions.add(serializationLibraryOpt.defaultValue(serializationLibrary.name())); - } - - @Override - public CodegenType getTag() { - return CodegenType.CLIENT; - } - - @Override - public String getName() { - return "tidal-kotlin"; - } - - @Override - public String getHelp() { - return "Generates a Kotlin client for our beloved TIDAL overlords."; - } - - public boolean getGenerateRoomModels() { - return generateRoomModels; - } - - public boolean getOmitGradleWrapper() { - return omitGradleWrapper; - } - - public boolean getGenerateOneOfAnyOfWrappers() { - return generateOneOfAnyOfWrappers; - } - - public void setGenerateRoomModels(Boolean generateRoomModels) { - this.generateRoomModels = generateRoomModels; - } - - public void setUseRxJava3(boolean useRxJava3) { - if (useRxJava3) { - this.doNotUseRxAndCoroutines = false; - this.useCoroutines = false; - } - this.useRxJava3 = useRxJava3; - } - - public void setDoNotUseRxAndCoroutines(boolean doNotUseRxAndCoroutines) { - if (doNotUseRxAndCoroutines) { - this.useRxJava3 = false; - this.useCoroutines = false; - } - this.doNotUseRxAndCoroutines = doNotUseRxAndCoroutines; - } - - public void setUseCoroutines(boolean useCoroutines) { - if (useCoroutines) { - this.useRxJava3 = false; - this.doNotUseRxAndCoroutines = false; - } - this.useCoroutines = useCoroutines; - } - - /** - * Sets the serialization engine for Kotlin - * - * @param enumSerializationLibrary The string representation of the serialization library as defined by - * {@link SERIALIZATION_LIBRARY_TYPE} - */ - public void setSerializationLibrary(final String enumSerializationLibrary) { - try { - this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.valueOf(enumSerializationLibrary); - } catch (IllegalArgumentException ex) { - StringBuilder sb = new StringBuilder(enumSerializationLibrary + " is an invalid enum property naming option. Please choose from:"); - for (SERIALIZATION_LIBRARY_TYPE t : SERIALIZATION_LIBRARY_TYPE.values()) { - sb.append("\n ").append(t.name()); - } - throw new RuntimeException(sb.toString()); - } - } - - @Override - public String modelFilename(String templateName, String modelName) { - String suffix = modelTemplateFiles().get(templateName); - // If this was a proper template method, i wouldn't have to make myself throw up by doing this.... - if (getGenerateRoomModels() && suffix.startsWith("RoomModel")) { - return roomModelFileFolder() + File.separator + toModelFilename(modelName) + suffix; - } else { - return modelFileFolder() + File.separator + toModelFilename(modelName) + suffix; - } - } - - public String roomModelFileFolder() { - return outputFolder + File.separator + sourceFolder + File.separator + roomModelPackage.replace('.', File.separatorChar); - } - - @Override - public void processOpts() { - if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { - setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); - } else { - // Set the value to defaults if we haven't overridden - if (MULTIPLATFORM.equals(getLibrary())) { - setSourceFolder("src/commonMain/kotlin"); - } else if (JVM_VOLLEY.equals(getLibrary())) { - // Android plugin wants it's source in java - setSourceFolder("src/main/java"); - } else { - setSourceFolder(super.sourceFolder); - } - additionalProperties.put(CodegenConstants.SOURCE_FOLDER, this.sourceFolder); - } - - super.processOpts(); - - boolean hasRx3 = additionalProperties.containsKey(USE_RX_JAVA3); - boolean hasCoroutines = additionalProperties.containsKey(USE_COROUTINES); - int optionCount = 0; - if (hasRx3) { - optionCount++; - } - if (hasCoroutines) { - optionCount++; - } - boolean hasConflict = optionCount > 1; - - // RxJava & Coroutines - if (hasConflict) { - LOGGER.warn("You specified RxJava versions 1 and 2 and 3 or Coroutines together, please choose one of them."); - } else if (hasRx3) { - this.setUseRxJava3(Boolean.parseBoolean(additionalProperties.get(USE_RX_JAVA3).toString())); - } else if (hasCoroutines) { - this.setUseCoroutines(Boolean.parseBoolean(additionalProperties.get(USE_COROUTINES).toString())); - } - - if (!hasRx3 && !hasCoroutines) { - setDoNotUseRxAndCoroutines(true); - additionalProperties.put(DO_NOT_USE_RX_AND_COROUTINES, true); - } - - // infrastructure destination folder - final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", "/"); - authFolder = (sourceFolder + File.separator + packageName + File.separator + "auth").replace(".", "/"); - - // request destination folder - final String requestFolder = (sourceFolder + File.separator + packageName + File.separator + "request").replace(".", "/"); - - // auth destination folder - final String authFolder = (sourceFolder + File.separator + packageName + File.separator + "auth").replace(".", "/"); - - // additional properties - if (additionalProperties.containsKey(DATE_LIBRARY)) { - setDateLibrary(additionalProperties.get(DATE_LIBRARY).toString()); - } - - if (additionalProperties.containsKey(REQUEST_DATE_CONVERTER)) { - setRequestDateConverter(additionalProperties.get(REQUEST_DATE_CONVERTER).toString()); - } - - if (additionalProperties.containsKey(OMIT_GRADLE_WRAPPER)) { - setOmitGradleWrapper(Boolean.parseBoolean(additionalProperties.get(OMIT_GRADLE_WRAPPER).toString())); - } - - if (additionalProperties.containsKey(CodegenConstants.SERIALIZATION_LIBRARY)) { - setSerializationLibrary((String) additionalProperties.get(CodegenConstants.SERIALIZATION_LIBRARY)); - additionalProperties.put(this.serializationLibrary.name(), true); - } else { - additionalProperties.put(this.serializationLibrary.name(), true); - } - -// if (additionalProperties.containsKey(GENERATE_ONEOF_ANYOF_WRAPPERS)) { -// setGenerateOneOfAnyOfWrappers(Boolean.parseBoolean(additionalProperties.get(GENERATE_ONEOF_ANYOF_WRAPPERS).toString())); -// } - setGenerateOneOfAnyOfWrappers(true); - - if (additionalProperties.containsKey(FAIL_ON_UNKNOWN_PROPERTIES)) { - setFailOnUnknownProperties(Boolean.parseBoolean(additionalProperties.get(FAIL_ON_UNKNOWN_PROPERTIES).toString())); - } else { - additionalProperties.put(FAIL_ON_UNKNOWN_PROPERTIES, false); - setFailOnUnknownProperties(false); - } - - commonSupportingFiles(); - processJVMRetrofit2Library(infrastructureFolder); - -// switch (getLibrary()) { -// case JVM_KTOR: -// processJVMKtorLibrary(infrastructureFolder); -// break; -// case JVM_OKHTTP4: -// processJVMOkHttpLibrary(infrastructureFolder); -// break; -// case JVM_VOLLEY: -// processJVMVolleyLibrary(infrastructureFolder, requestFolder, authFolder); -// break; -// case JVM_RETROFIT2: -// processJVMRetrofit2Library(infrastructureFolder); -// break; -// case JVM_SPRING_WEBCLIENT: -// processJvmSpringWebClientLibrary(infrastructureFolder); -// break; -// case JVM_SPRING_RESTCLIENT: -// processJvmSpringRestClientLibrary(infrastructureFolder); -// break; -// case MULTIPLATFORM: -// processMultiplatformLibrary(infrastructureFolder); -// break; -// case JVM_VERTX: -// processJVMVertXLibrary(infrastructureFolder); -// break; -// default: -// break; -// } - - processDateLibrary(); - processRequestDateConverter(); - - if (additionalProperties.containsKey(COLLECTION_TYPE)) { - setCollectionType(additionalProperties.get(COLLECTION_TYPE).toString()); - } - - if (CollectionType.LIST.value.equals(collectionType)) { - if (isModelMutable()) { - typeMapping.put("array", "kotlin.collections.MutableList"); - typeMapping.put("list", "kotlin.collections.MutableList"); - } else { - typeMapping.put("array", "kotlin.collections.List"); - typeMapping.put("list", "kotlin.collections.List"); - } - additionalProperties.put("isList", true); - } - -// if (usesRetrofit2Library()) { -// boolean hasOAuthMethods = ProcessUtils.hasOAuthMethods(openAPI); -// -// if (hasOAuthMethods) { -// supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); -// supportingFiles.add(new SupportingFile("auth/OAuthFlow.kt.mustache", authFolder, "OAuthFlow.kt")); -// supportingFiles.add(new SupportingFile("auth/OAuthOkHttpClient.kt.mustache", authFolder, "OAuthOkHttpClient.kt")); -// } -// -// if (hasOAuthMethods || ProcessUtils.hasApiKeyMethods(openAPI)) { -// supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); -// } -// -// if (ProcessUtils.hasHttpBearerMethods(openAPI)) { -// supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); -// } -// -// if (ProcessUtils.hasHttpBasicMethods(openAPI)) { -// supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); -// } -// } - - additionalProperties.put("sanitizeGeneric", (Mustache.Lambda) (fragment, writer) -> { - String content = fragment.execute(); - for (final String s : List.of("<", ">", ",", " ", ".")) { - content = content.replace(s, ""); - } - writer.write(content); - }); - } - - private void processDateLibrary() { - if (DateLibrary.THREETENBP.value.equals(dateLibrary) || DateLibrary.THREETENBP_LOCALDATETIME.value.equals(dateLibrary)) { - processThreeTenBpDate(dateLibrary); - } else if (DateLibrary.STRING.value.equals(dateLibrary)) { - processStringDate(); - } else if (DateLibrary.JAVA8.value.equals(dateLibrary) || DateLibrary.JAVA8_LOCALDATETIME.value.equals(dateLibrary)) { - processJava8Date(dateLibrary); - } else if (DateLibrary.KOTLINX_DATETIME.value.equals(dateLibrary)) { - processKotlinxDate(); - } - } - - private void processRequestDateConverter() { - if (RequestDateConverter.TO_JSON.value.equals(requestDateConverter)) { - additionalProperties.put(RequestDateConverter.TO_JSON.value, true); - } else if (RequestDateConverter.TO_STRING.value.equals(requestDateConverter)) { - additionalProperties.put(RequestDateConverter.TO_STRING.value, true); - } - } - - private void processThreeTenBpDate(String dateLibrary) { - additionalProperties.put(DateLibrary.THREETENBP.value, true); - typeMapping.put("date", "LocalDate"); - importMapping.put("LocalDate", "org.threeten.bp.LocalDate"); - defaultIncludes.add("org.threeten.bp.LocalDate"); - - if (dateLibrary.equals(DateLibrary.THREETENBP.value)) { - typeMapping.put("date-time", "org.threeten.bp.OffsetDateTime"); - typeMapping.put("DateTime", "OffsetDateTime"); - importMapping.put("OffsetDateTime", "org.threeten.bp.OffsetDateTime"); - defaultIncludes.add("org.threeten.bp.OffsetDateTime"); - } else if (dateLibrary.equals(DateLibrary.THREETENBP_LOCALDATETIME.value)) { - typeMapping.put("date-time", "org.threeten.bp.LocalDateTime"); - typeMapping.put("DateTime", "LocalDateTime"); - importMapping.put("LocalDateTime", "org.threeten.bp.LocalDateTime"); - defaultIncludes.add("org.threeten.bp.LocalDateTime"); - } - } - - private void processStringDate() { - typeMapping.put("date-time", "kotlin.String"); - typeMapping.put("date", "kotlin.String"); - typeMapping.put("Date", "kotlin.String"); - typeMapping.put("DateTime", "kotlin.String"); - } - - private void processJava8Date(String dateLibrary) { - additionalProperties.put(DateLibrary.JAVA8.value, true); - - if (dateLibrary.equals(DateLibrary.JAVA8.value)) { - typeMapping.put("date-time", "java.time.OffsetDateTime"); - typeMapping.put("DateTime", "OffsetDateTime"); - importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); - } else if (dateLibrary.equals(DateLibrary.JAVA8_LOCALDATETIME.value)) { - typeMapping.put("date-time", "java.time.LocalDateTime"); - typeMapping.put("DateTime", "LocalDateTime"); - importMapping.put("LocalDateTime", "java.time.LocalDateTime"); - } - } - - private void processKotlinxDate() { - additionalProperties.put(DateLibrary.KOTLINX_DATETIME.value, true); - - typeMapping.put("date-time", "Instant"); - typeMapping.put("date", "LocalDate"); - typeMapping.put("time", "LocalTime"); - - typeMapping.put("DateTime", "Instant"); - typeMapping.put("Date", "LocalDate"); - typeMapping.put("Time", "LocalTime"); - - importMapping.put("Instant", "kotlinx.datetime.Instant"); - importMapping.put("LocalDate", "kotlinx.datetime.LocalDate"); - importMapping.put("LocalTime", "kotlinx.datetime.LocalTime"); - } - - private void processJVMRetrofit2Library(String infrastructureFolder) { - additionalProperties.put(JVM, true); - additionalProperties.put(JVM_RETROFIT2, true); - supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); - supportingFiles.add(new SupportingFile("infrastructure/ResponseExt.kt.mustache", infrastructureFolder, "ResponseExt.kt")); - supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); - addSupportingSerializerAdapters(infrastructureFolder); - } - - private void processJVMVolleyLibrary(String infrastructureFolder, String requestFolder, String authFolder) { - - additionalProperties.put(JVM, true); - additionalProperties.put(JVM_VOLLEY, true); - - if (additionalProperties.containsKey(GENERATE_ROOM_MODELS)) { - this.setGenerateRoomModels(convertPropertyToBooleanAndWriteBack(GENERATE_ROOM_MODELS)); - // Hide this option behind a property getter and setter in case we need to check it elsewhere - if (getGenerateRoomModels()) { - modelTemplateFiles.put("model_room.mustache", "RoomModel.kt"); - supportingFiles.add(new SupportingFile("infrastructure/ITransformForStorage.mustache", infrastructureFolder, "ITransformForStorage.kt")); - - } - } else { - additionalProperties.put(GENERATE_ROOM_MODELS, generateRoomModels); - } - - if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - if (!additionalProperties.containsKey(ROOM_MODEL_PACKAGE)) - this.setRoomModelPackage(packageName + ".models.room"); - else - this.setRoomModelPackage(additionalProperties.get(ROOM_MODEL_PACKAGE).toString()); - } - additionalProperties.put(ROOM_MODEL_PACKAGE, roomModelPackage); - - supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); - - // We have auth related partial files, so they can be overridden, but don't generate them explicitly - supportingFiles.add(new SupportingFile("request/GsonRequest.mustache", requestFolder, "GsonRequest.kt")); - supportingFiles.add(new SupportingFile("request/IRequestFactory.mustache", requestFolder, "IRequestFactory.kt")); - supportingFiles.add(new SupportingFile("request/RequestFactory.mustache", requestFolder, "RequestFactory.kt")); - supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt")); - -// if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.gson) { -// throw new RuntimeException("This library currently only supports gson serialization. Try adding '--additional-properties serializationLibrary=gson' to your command."); -// } - addSupportingSerializerAdapters(infrastructureFolder); - supportingFiles.remove(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); - - } - - private void addSupportingSerializerAdapters(final String infrastructureFolder) { - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); - -// switch (getSerializationLibrary()) { -// case moshi: -// if (enumUnknownDefaultCase) { -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/SerializerHelper.kt.mustache", infrastructureFolder, "SerializerHelper.kt")); -// } -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); -// addKotlinxDateTimeAdapters(infrastructureFolder); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); -// break; -// -// case gson: -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); -// supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); -// addKotlinxDateTimeAdapters(infrastructureFolder); -// break; -// -// case jackson: -// break; -// -// case kotlinx_serialization: - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache", infrastructureFolder, "AtomicBooleanAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache", infrastructureFolder, "AtomicIntegerAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/AtomicLongAdapter.kt.mustache", infrastructureFolder, "AtomicLongAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URIAdapter.kt.mustache", infrastructureFolder, "URIAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/URLAdapter.kt.mustache", infrastructureFolder, "URLAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigIntegerAdapter.kt.mustache", infrastructureFolder, "BigIntegerAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/BigDecimalAdapter.kt.mustache", infrastructureFolder, "BigDecimalAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateAdapter.kt.mustache", infrastructureFolder, "LocalDateAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache", infrastructureFolder, "LocalDateTimeAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache", infrastructureFolder, "OffsetDateTimeAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/UUIDAdapter.kt.mustache", infrastructureFolder, "UUIDAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/StringBuilderAdapter.kt.mustache", infrastructureFolder, "StringBuilderAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/proguard-rules.pro.mustache", "", "proguard-rules.pro")); -// break; -// } - } - - private void addKotlinxDateTimeAdapters(final String infrastructureFolder) { - if (DateLibrary.KOTLINX_DATETIME.value.equals(dateLibrary)) { - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/InstantAdapter.kt.mustache", infrastructureFolder, "InstantAdapter.kt")); - supportingFiles.add(new SupportingFile("jvm-common/infrastructure/LocalTimeAdapter.kt.mustache", infrastructureFolder, "LocalTimeAdapter.kt")); - } - } - - private void processJVMKtorLibrary(final String infrastructureFolder) { - // in future kotlinx.serialization may be added -// if (this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.gson && this.serializationLibrary != SERIALIZATION_LIBRARY_TYPE.jackson) { - this.serializationLibrary = SERIALIZATION_LIBRARY_TYPE.jackson; -// } - - additionalProperties.put(JVM, true); - additionalProperties.put(JVM_KTOR, true); - - defaultIncludes.add("io.ktor.client.request.forms.InputProvider"); - - importMapping.put("InputProvider", "io.ktor.client.request.forms.InputProvider"); - - supportingFiles.add(new SupportingFile("infrastructure/ApiAbstractions.kt.mustache", infrastructureFolder, "ApiAbstractions.kt")); - supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); - supportingFiles.add(new SupportingFile("infrastructure/HttpResponse.kt.mustache", infrastructureFolder, "HttpResponse.kt")); - supportingFiles.add(new SupportingFile("infrastructure/RequestConfig.kt.mustache", infrastructureFolder, "RequestConfig.kt")); - supportingFiles.add(new SupportingFile("infrastructure/RequestMethod.kt.mustache", infrastructureFolder, "RequestMethod.kt")); - - supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); - supportingFiles.add(new SupportingFile("auth/Authentication.kt.mustache", authFolder, "Authentication.kt")); - supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); - supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); - supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); - } - - /** - * Process Vert.x client options - * - * @param infrastructureFolder infrastructure destination folder - */ - private void processJVMVertXLibrary(final String infrastructureFolder) { - supportingFiles.add(new SupportingFile("infrastructure/ApiAbstractions.kt.mustache", infrastructureFolder, "ApiAbstractions.kt")); - supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); - supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt")); - supportingFiles.add(new SupportingFile("infrastructure/ApiResponse.kt.mustache", infrastructureFolder, "ApiResponse.kt")); - addSupportingSerializerAdapters(infrastructureFolder); - - additionalProperties.put(JVM, true); - additionalProperties.put(JVM_VERTX, true); - } - - private void processJVMOkHttpLibrary(final String infrastructureFolder) { - commonJvmMultiplatformSupportingFiles(infrastructureFolder); - addSupportingSerializerAdapters(infrastructureFolder); - - additionalProperties.put(JVM, true); - additionalProperties.put(JVM_OKHTTP, true); - - if (JVM_OKHTTP4.equals(getLibrary())) { - additionalProperties.put(JVM_OKHTTP4, true); - } - -// supportedLibraries.put(JVM_OKHTTP, "A workaround to use the same template folder for both 'jvm-okhttp3' and 'jvm-okhttp4'."); - setLibrary(JVM_OKHTTP); - - // jvm specific supporting files - supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt")); - supportingFiles.add(new SupportingFile("infrastructure/ResponseExtensions.kt.mustache", infrastructureFolder, "ResponseExtensions.kt")); - supportingFiles.add(new SupportingFile("infrastructure/ApiResponse.kt.mustache", infrastructureFolder, "ApiResponse.kt")); - } - - private void proccessJvmSpring(final String infrastructureFolder) { - if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.jackson) { - throw new RuntimeException("This library currently only supports jackson serialization. Try adding '--additional-properties serializationLibrary=jackson' to your command."); - } - - commonJvmMultiplatformSupportingFiles(infrastructureFolder); - addSupportingSerializerAdapters(infrastructureFolder); - - additionalProperties.put(JVM_SPRING, true); - additionalProperties.put(JVM, true); - } - - private void processJvmSpringWebClientLibrary(final String infrastructureFolder) { - proccessJvmSpring(infrastructureFolder); - additionalProperties.put(JVM_SPRING_WEBCLIENT, true); - } - - private void processJvmSpringRestClientLibrary(final String infrastructureFolder) { - if (additionalProperties.getOrDefault(USE_SPRING_BOOT3, false).equals(false)) { - throw new RuntimeException("This library must use Spring Boot 3. Try adding '--additional-properties useSpringBoot3=true' to your command."); - } - - proccessJvmSpring(infrastructureFolder); - additionalProperties.put(JVM_SPRING_RESTCLIENT, true); - } - - private void processMultiplatformLibrary(final String infrastructureFolder) { - commonJvmMultiplatformSupportingFiles(infrastructureFolder); - additionalProperties.put(MULTIPLATFORM, true); - - if (!DateLibrary.STRING.value.equals(dateLibrary) && !DateLibrary.KOTLINX_DATETIME.value.equals(dateLibrary)) { - throw new RuntimeException("Multiplatform only supports string and kotlinx-datetime. Try adding '--additional-properties dateLibrary=kotlinx-datetime' to your command."); - } - - setRequestDateConverter(RequestDateConverter.TO_STRING.value); - - // multiplatform default includes - defaultIncludes.add("io.ktor.client.request.forms.InputProvider"); - defaultIncludes.add(packageName + ".infrastructure.Base64ByteArray"); - defaultIncludes.add(packageName + ".infrastructure.OctetByteArray"); - - // multiplatform type mapping - typeMapping.put("number", "kotlin.Double"); - typeMapping.put("file", "OctetByteArray"); - typeMapping.put("binary", "OctetByteArray"); - typeMapping.put("ByteArray", "Base64ByteArray"); - typeMapping.put("object", "kotlin.String"); // kotlin.Any not serializable - - // multiplatform import mapping - importMapping.put("BigDecimal", "kotlin.Double"); - importMapping.put("UUID", "kotlin.String"); - importMapping.put("URI", "kotlin.String"); - importMapping.put("InputProvider", "io.ktor.client.request.forms.InputProvider"); - importMapping.put("File", packageName + ".infrastructure.OctetByteArray"); - importMapping.put("Timestamp", "kotlin.String"); - importMapping.put("LocalDateTime", "kotlin.String"); - importMapping.put("LocalDate", "kotlin.String"); - importMapping.put("LocalTime", "kotlin.String"); - importMapping.put("Base64ByteArray", packageName + ".infrastructure.Base64ByteArray"); - importMapping.put("OctetByteArray", packageName + ".infrastructure.OctetByteArray"); - - // multiplatform specific supporting files - supportingFiles.add(new SupportingFile("infrastructure/Base64ByteArray.kt.mustache", infrastructureFolder, "Base64ByteArray.kt")); - supportingFiles.add(new SupportingFile("infrastructure/Bytes.kt.mustache", infrastructureFolder, "Bytes.kt")); - supportingFiles.add(new SupportingFile("infrastructure/HttpResponse.kt.mustache", infrastructureFolder, "HttpResponse.kt")); - supportingFiles.add(new SupportingFile("infrastructure/OctetByteArray.kt.mustache", infrastructureFolder, "OctetByteArray.kt")); - - // multiplatform specific auth - supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.kt.mustache", authFolder, "ApiKeyAuth.kt")); - supportingFiles.add(new SupportingFile("auth/Authentication.kt.mustache", authFolder, "Authentication.kt")); - supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.kt.mustache", authFolder, "HttpBasicAuth.kt")); - supportingFiles.add(new SupportingFile("auth/HttpBearerAuth.kt.mustache", authFolder, "HttpBearerAuth.kt")); - supportingFiles.add(new SupportingFile("auth/OAuth.kt.mustache", authFolder, "OAuth.kt")); - - // multiplatform specific testing files - supportingFiles.add(new SupportingFile("commonTest/Coroutine.kt.mustache", "src/commonTest/kotlin/util", "Coroutine.kt")); - supportingFiles.add(new SupportingFile("iosTest/Coroutine.kt.mustache", "src/iosTest/kotlin/util", "Coroutine.kt")); - supportingFiles.add(new SupportingFile("jsTest/Coroutine.kt.mustache", "src/jsTest/kotlin/util", "Coroutine.kt")); - supportingFiles.add(new SupportingFile("jvmTest/Coroutine.kt.mustache", "src/jvmTest/kotlin/util", "Coroutine.kt")); - } - - - private void commonJvmMultiplatformSupportingFiles(String infrastructureFolder) { - 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/PartConfig.kt.mustache", infrastructureFolder, "PartConfig.kt")); - supportingFiles.add(new SupportingFile("infrastructure/RequestConfig.kt.mustache", infrastructureFolder, "RequestConfig.kt")); - supportingFiles.add(new SupportingFile("infrastructure/RequestMethod.kt.mustache", infrastructureFolder, "RequestMethod.kt")); - } - - private void commonSupportingFiles() { - LOGGER.info("adding file"); - supportingFiles.add(new SupportingFile("Utils.kt.mustache", (sourceFolder + "." + modelPackage).replace(".", File.separator), "Utils.kt")); - supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - if (getLibrary().equals(MULTIPLATFORM)) { - supportingFiles.add(new SupportingFile("build.gradle.kts.mustache", "", "build.gradle.kts")); - supportingFiles.add(new SupportingFile("settings.gradle.kts.mustache", "", "settings.gradle.kts")); - } else if (getLibrary().equals(JVM_VOLLEY)) { - supportingFiles.add(new SupportingFile("build.mustache", "", "build.gradle")); - supportingFiles.add(new SupportingFile("gradle.properties.mustache", "", "gradle.properties")); - supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle")); - supportingFiles.add(new SupportingFile("manifest.mustache", "", "src/main/AndroidManifest.xml")); - } else { - supportingFiles.add(new SupportingFile("build.gradle.mustache", "", "build.gradle")); - supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle")); - } - - // gradle wrapper supporting files - if (!getOmitGradleWrapper()) { - supportingFiles.add(new SupportingFile("gradlew.mustache", "", "gradlew")); - supportingFiles.add(new SupportingFile("gradlew.bat.mustache", "", "gradlew.bat")); - supportingFiles.add(new SupportingFile("gradle-wrapper.properties.mustache", "gradle.wrapper".replace(".", File.separator), "gradle-wrapper.properties")); - supportingFiles.add(new SupportingFile("gradle-wrapper.jar", "gradle.wrapper".replace(".", File.separator), "gradle-wrapper.jar")); - } - } - - @Override - public ModelsMap postProcessModels(ModelsMap objs) { - ModelsMap objects = super.postProcessModels(objs); - List allModels = objects.getModels(); - for (ModelMap mo : allModels) { - CodegenModel cm = mo.getModel(); - LOGGER.info("model: " + cm.classname); - } - for (ModelMap mo : allModels) { - CodegenModel cm = mo.getModel(); - - if (getGenerateRoomModels() || getGenerateOneOfAnyOfWrappers()) { - cm.vendorExtensions.put("x-has-data-class-body", true); - } - - // escape the variable base name for use as a string literal - List vars = Stream.of( - cm.vars, - cm.allVars, - cm.optionalVars, - cm.requiredVars, - cm.readOnlyVars, - cm.readWriteVars, - cm.parentVars - ) - .flatMap(List::stream) - .collect(Collectors.toList()); - - for (CodegenProperty var : vars) { - var.vendorExtensions.put(VENDOR_EXTENSION_BASE_NAME_LITERAL, var.baseName.replace("$", "\\$")); - } - } - - return objects; - } - - @Override - public Map postProcessAllModels(Map objs) { - LOGGER.info("postProcessAllModels"); - objs = super.postProcessAllModels(objs); - objs = super.updateAllModels(objs); - - Map> resourceRelationShips = new HashMap<>(); - BiConsumer addToResourceRelationships = (key, value) -> { - resourceRelationShips.computeIfAbsent(key, k -> new ArrayList<>()).add(value); - }; - - // Find all oneOf parents - objs.forEach((key, value) -> { - List models = value.getModels(); - for (ModelMap modelMap : models) { - CodegenModel codegenModel = modelMap.getModel(); - - if (!codegenModel.oneOf.isEmpty()) { - String className = codegenModel.classname; - codegenModel.oneOf.forEach(oneOfClass -> addToResourceRelationships.accept(className, oneOfClass)); - codegenModel.vendorExtensions.put("x-is-oneof-parent", true); - } else { - // this field needs to be null to prevent mustache from getting triggered by an empty set - codegenModel.oneOf = null; - } - } - }); - - List combinedList = resourceRelationShips.values().stream() - .flatMap(List::stream) - .collect(Collectors.toList()); - - // Find all oneOf children and assign relationships - objs.forEach((key, value) -> { - List models = value.getModels(); - for (ModelMap modelMap : models) { - CodegenModel codegenModel = modelMap.getModel(); - String className = codegenModel.classname; - - if (combinedList.contains(className)) { - codegenModel.vendorExtensions.put("x-has-oneof-parent", true); - codegenModel.vendorExtensions.put("x-has-serializable-type", "hello"); - int index = codegenModel.classname.indexOf("Resource"); - if (index != -1) { - String serializableType = - Character.toLowerCase(codegenModel.classname.charAt(0)) + codegenModel.classname.substring(1, index); - codegenModel.vendorExtensions.put("x-has-serializable-type", serializableType); - } - - codegenModel.allVars.stream() - .filter(p -> Objects.equals(p.name, "type")) - .forEach(p -> { - LOGGER.info("Found type field in " + className); - p.vendorExtensions.put("x-is-transient", true); - LOGGER.info("VendorExtensions: " + p.vendorExtensions); - LOGGER.info("model VendorExtensions: " + codegenModel.vendorExtensions); - }); -// codegenModel.allVars.removeIf(p -> p.name.equals("type")); - // Find all parents for the current class and add to allParents - codegenModel.allParents = resourceRelationShips.entrySet().stream() - .filter(entry -> entry.getValue().contains(className)) - .map(Map.Entry::getKey).collect(Collectors.toList()); - } - } - }); - return objs; - } - - private boolean usesRetrofit2Library() { - return getLibrary() != null && getLibrary().contains(JVM_RETROFIT2); - } - - @Override - public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { - super.postProcessOperationsWithModels(objs, allModels); - OperationMap operations = objs.getOperations(); - boolean isResponseFile = false; - if (operations != null) { - List ops = operations.getOperation(); - for (CodegenOperation operation : ops) { - isResponseFile = isResponseFile || operation.isResponseFile; - - if (JVM_RETROFIT2.equals(getLibrary()) && StringUtils.isNotEmpty(operation.path) && operation.path.startsWith("/")) { - operation.path = operation.path.substring(1); - } - - if (JVM_OKHTTP.equals(getLibrary()) || JVM_OKHTTP4.equals(getLibrary())) { - // Ideally we would do content negotiation to choose the best mediatype, but that would be a next step. - // For now we take the first mediatype we can parse and send that. - Predicate> isSerializable = typeMapping -> { - String mediaTypeValue = typeMapping.get("mediaType"); - if (mediaTypeValue == null) - return false; - // match on first part in mediaTypes like 'application/json; charset=utf-8' - int endIndex = mediaTypeValue.indexOf(';'); - String mediaType = (endIndex == -1 - ? mediaTypeValue - : mediaTypeValue.substring(0, endIndex) - ).trim(); - return "multipart/form-data".equals(mediaType) - || "application/x-www-form-urlencoded".equals(mediaType) - || (mediaType.startsWith("application/") && (mediaType.endsWith("json") || mediaType.endsWith("octet-stream"))); - }; - operation.consumes = operation.consumes == null ? null : operation.consumes.stream() - .filter(isSerializable) - .limit(1) - .collect(Collectors.toList()); - operation.hasConsumes = operation.consumes != null && !operation.consumes.isEmpty(); - - operation.produces = operation.produces == null ? null : operation.produces.stream() - .filter(isSerializable) - .collect(Collectors.toList()); - operation.hasProduces = operation.produces != null && !operation.produces.isEmpty(); - } - - // set multipart against all relevant operations - if (operation.hasConsumes == Boolean.TRUE) { - if (isMultipartType(operation.consumes)) { - operation.isMultipart = Boolean.TRUE; - } - } - - // import okhttp3.MultipartBody if any parameter is a file - for (CodegenParameter param : operation.allParams) { - if (Boolean.TRUE.equals(param.isFile)) { - operations.put("x-kotlin-multipart-import", true); - } - - if (param.isQueryParam && "form".equals(param.style) && param.isExplode && param.isModel) { - // query parameter (style: form, explode) referencing models need to import - // models defined in the properties of the models - operations.put("x-kotlin-import-models", true); - } - } - - if (usesRetrofit2Library() && StringUtils.isNotEmpty(operation.path) && operation.path.startsWith("/")) { - operation.path = operation.path.substring(1); - } - - // sorting operation parameters to make sure path params are parsed before query params - if (operation.allParams != null) { - sort(operation.allParams, (one, another) -> { - if (one.isPathParam && another.isQueryParam) { - return -1; - } - if (one.isQueryParam && another.isPathParam) { - return 1; - } - - return 0; - }); - } - - // modify the data type of binary form parameters to a more friendly type for ktor builds - if ((JVM_KTOR.equals(getLibrary()) || MULTIPLATFORM.equals(getLibrary())) && operation.allParams != null) { - for (CodegenParameter param : operation.allParams) { - if (param.dataFormat != null && param.dataFormat.equals("binary")) { - param.baseType = param.dataType = "io.ktor.client.request.forms.InputProvider"; - } - } - } - } - } - objs.put("isResponseFile", isResponseFile); - return objs; - } - - private static boolean isMultipartType(List> consumes) { - Map firstType = consumes.get(0); - if (firstType != null) { - return "multipart/form-data".equals(firstType.get("mediaType")); - } - return false; - } - - @Override - public void postProcess() { - System.out.println("################################################################################"); - System.out.println("# Thanks for using OpenAPI Generator. #"); - System.out.println("# Please consider donation to help us maintain this project \uD83D\uDE4F #"); - System.out.println("# https://opencollective.com/openapi_generator/donate #"); - System.out.println("# #"); - System.out.println("# This generator's contributed by Jim Schubert (https://github.com/jimschubert)#"); - System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #"); - System.out.println("################################################################################"); - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache deleted file mode 100644 index 5d4f0e1cdf81..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/README.mustache +++ /dev/null @@ -1,113 +0,0 @@ -# {{packageName}} - Kotlin client library for {{appName}} - -{{#appDescriptionWithNewLines}} -{{{.}}} -{{/appDescriptionWithNewLines}} - -## Overview -This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate an API client. - -- API version: {{appVersion}} -- Package version: {{packageVersion}} -{{^hideGenerationTimestamp}} -- Build date: {{generatedDate}} -{{/hideGenerationTimestamp}} -- Generator version: {{generatorVersion}} -- Build package: {{generatorClass}} -{{#infoUrl}} -For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) -{{/infoUrl}} - -## Requires - -{{#jvm}} -* Kotlin 1.7.21 -* Gradle 7.5 -{{/jvm}} -{{#multiplatform}} -* Kotlin 1.5.10 -{{/multiplatform}} - -## Build - -{{#jvm}} -First, create the gradle wrapper script: - -``` -gradle wrapper -``` - -Then, run: - -{{/jvm}} -``` -./gradlew check assemble -``` - -This runs all tests and packages the library. - -## Features/Implementation Notes - -* Supports JSON inputs/outputs, File inputs, and Form inputs. -* Supports collection formats for query parameters: csv, tsv, ssv, pipes. -* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in OpenAPI definitions. -{{#jvm}}* Implementation of ApiClient is intended to reduce method counts, specifically to benefit Android targets.{{/jvm}} - -{{#generateApiDocs}} - -## Documentation for API Endpoints - -All URIs are relative to *{{{basePath}}}* - -| Class | Method | HTTP request | Description | -| ------------ | ------------- | ------------- | ------------- | -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}| *{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{{summary}}} | -{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} -{{/generateApiDocs}} - -{{#generateModelDocs}} - -## Documentation for Models - -{{#modelPackage}} -{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) -{{/model}}{{/models}} -{{/modelPackage}} -{{^modelPackage}} -No model defined in this package -{{/modelPackage}} -{{/generateModelDocs}} - - -## Documentation for Authorization - -{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} -{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} -{{#authMethods}} - -### {{name}} - -{{#isApiKey}}- **Type**: API key -- **API key parameter name**: {{keyParamName}} -- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} -{{/isApiKey}} -{{#isBasicBasic}}- **Type**: HTTP basic authentication -{{/isBasicBasic}} -{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} -{{/isBasicBearer}} -{{#isHttpSignature}}- **Type**: HTTP signature authentication -{{/isHttpSignature}} -{{#isOAuth}}- **Type**: OAuth -- **Flow**: {{flow}} -- **Authorization URL**: {{authorizationUrl}} -- **Scopes**: {{^scopes}}N/A{{/scopes}} -{{#scopes}} - {{scope}}: {{description}} -{{/scopes}} -{{/isOAuth}} - -{{/authMethods}}{{#infoEmail}} - -## Author - -{{{.}}} -{{/infoEmail}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache deleted file mode 100644 index c5e9419da20c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/Utils.kt.mustache +++ /dev/null @@ -1,19 +0,0 @@ -package {{modelPackage}} - -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic - -fun getOneOfSerializer() = SerializersModule { -{{#models}} -{{#model}} -{{#oneOf.size}} - polymorphic({{classname}}::class) { -{{#oneOf}} - subclass({{.}}::class, {{.}}.serializer()) -{{/oneOf}} - } -{{/oneOf.size}} -{{/model}} -{{/models}} -} - diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache deleted file mode 100644 index f4871c02cc2e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/additionalModelTypeAnnotations.mustache +++ /dev/null @@ -1,2 +0,0 @@ -{{#additionalModelTypeAnnotations}}{{{.}}} -{{/additionalModelTypeAnnotations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache deleted file mode 100644 index a745e7745f0c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/anyof_class.mustache +++ /dev/null @@ -1,241 +0,0 @@ -{{^multiplatform}} -{{#gson}} -{{#generateOneOfAnyOfWrappers}} -import com.google.gson.Gson -import com.google.gson.JsonElement -import com.google.gson.TypeAdapter -import com.google.gson.TypeAdapterFactory -import com.google.gson.reflect.TypeToken -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.annotations.JsonAdapter -{{/generateOneOfAnyOfWrappers}} -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -{{/moshi}} -{{#jackson}} -{{#enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue -{{/enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonProperty -{{#discriminator}} -import com.fasterxml.jackson.annotation.JsonSubTypes -import com.fasterxml.jackson.annotation.JsonTypeInfo -{{/discriminator}} -{{/jackson}} -{{#kotlinx_serialization}} -import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} -import kotlinx.serialization.SerialName -import kotlinx.serialization.Contextual -{{#enumUnknownDefaultCase}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -{{/enumUnknownDefaultCase}} -{{#hasEnums}} -{{/hasEnums}} -{{/kotlinx_serialization}} -{{#parcelizeModels}} -import android.os.Parcelable -import kotlinx.parcelize.Parcelize -{{/parcelizeModels}} -{{/multiplatform}} -{{#multiplatform}} -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* -{{/multiplatform}} -{{#serializableModel}} -import java.io.Serializable -{{/serializableModel}} -{{#generateRoomModels}} -import {{roomModelPackage}}.{{classname}}RoomModel -import {{packageName}}.infrastructure.ITransformForStorage -{{/generateRoomModels}} -import java.io.IOException - -/** - * {{{description}}} - * - */ -{{#parcelizeModels}} -@Parcelize -{{/parcelizeModels}} -{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} -{{#isDeprecated}} -@Deprecated(message = "This schema is deprecated.") -{{/isDeprecated}} -{{>additionalModelTypeAnnotations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}(var actualInstance: Any? = null) { - - class CustomTypeAdapterFactory : TypeAdapterFactory { - override fun create(gson: Gson, type: TypeToken): TypeAdapter? { - if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { - return null // this class only serializes '{{classname}}' and its subtypes - } - val elementAdapter = gson.getAdapter(JsonElement::class.java) - {{#composedSchemas}} - {{#anyOf}} - {{^isArray}} - {{^vendorExtensions.x-duplicated-data-type}} - val adapter{{{dataType}}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}::class.java)) - {{/vendorExtensions.x-duplicated-data-type}} - {{/isArray}} - {{#isArray}} - @Suppress("UNCHECKED_CAST") - val adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = gson.getDelegateAdapter(this, TypeToken.get(object : TypeToken<{{{dataType}}}>() {}.type)) as TypeAdapter<{{{dataType}}}> - {{/isArray}} - {{/anyOf}} - {{/composedSchemas}} - - @Suppress("UNCHECKED_CAST") - return object : TypeAdapter<{{classname}}?>() { - @Throws(IOException::class) - override fun write(out: JsonWriter,value: {{classname}}?) { - if (value?.actualInstance == null) { - elementAdapter.write(out, null) - return - } - - {{#composedSchemas}} - {{#anyOf}} - {{^vendorExtensions.x-duplicated-data-type}} - // check if the actual instance is of the type `{{{dataType}}}` - if (value.actualInstance is {{#isArray}}List<*>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) { - {{#isArray}} - val list = value.actualInstance as List - if (list.get(0) is {{{items.dataType}}}) { - val array = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonArray() - elementAdapter.write(out, array) - return - } - {{/isArray}} - {{^isArray}} - {{#isPrimitiveType}} - val primitive = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonPrimitive() - elementAdapter.write(out, primitive) - return - {{/isPrimitiveType}} - {{^isPrimitiveType}} - val element = adapter{{{dataType}}}.toJsonTree(value.actualInstance as {{{dataType}}}?) - elementAdapter.write(out, element) - return - {{/isPrimitiveType}} - {{/isArray}} - } - {{/vendorExtensions.x-duplicated-data-type}} - {{/anyOf}} - {{/composedSchemas}} - throw IOException("Failed to serialize as the type doesn't match anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}") - } - - @Throws(IOException::class) - override fun read(jsonReader: JsonReader): {{classname}} { - val jsonElement = elementAdapter.read(jsonReader) - val errorMessages = ArrayList() - var actualAdapter: TypeAdapter<*> - val ret = {{classname}}() - - {{#composedSchemas}} - {{#anyOf}} - {{^vendorExtensions.x-duplicated-data-type}} - {{^hasVars}} - // deserialize {{{dataType}}} - try { - // validate the JSON object to see if any exception is thrown - {{^isArray}} - {{#isNumber}} - require(jsonElement.getAsJsonPrimitive().isNumber()) { - String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) - } - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) - return ret - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - require(jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) - } - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) - return ret - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{{dataType}}}.validateJsonElement(jsonElement) - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) - return ret - {{/isPrimitiveType}} - {{/isNumber}} - {{/isArray}} - {{#isArray}} - require(jsonElement.isJsonArray) { - String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()) - } - - // validate array items - for(element in jsonElement.getAsJsonArray()) { - {{#items}} - {{#isNumber}} - require(jsonElement.getAsJsonPrimitive().isNumber) { - String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - require(element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}) { - String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{{dataType}}}.validateJsonElement(element) - {{/isPrimitiveType}} - {{/isNumber}} - {{/items}} - } - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) - return ret - {{/isArray}} - //log.log(Level.FINER, "Input data matches schema '{{{dataType}}}'") - } catch (e: Exception) { - // deserialization failed, continue - errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.message)) - //log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e) - } - {{/hasVars}} - {{#hasVars}} - // deserialize {{{.}}} - try { - // validate the JSON object to see if any exception is thrown - {{.}}.validateJsonElement(jsonElement) - log.log(Level.FINER, "Input data matches schema '{{{.}}}'") - actualAdapter = adapter{{.}} - ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) - return ret - } catch (e: Exception) { - // deserialization failed, continue - errorMessages.add(String.format("Deserialization for {{{.}}} failed with `%s`.", e.message)) - //log.log(Level.FINER, "Input data does not match schema '{{{.}}}'", e) - } - {{/hasVars}} - {{/vendorExtensions.x-duplicated-data-type}} - {{/anyOf}} - {{/composedSchemas}} - - throw IOException(String.format("Failed deserialization for {{classname}}: no schema match result. Detailed failure message for anyOf schemas: %s. JSON: %s", errorMessages, jsonElement.toString())) - } - }.nullSafe() as TypeAdapter - } - } -} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache deleted file mode 100644 index 16eafac78f43..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/api_doc.mustache +++ /dev/null @@ -1,92 +0,0 @@ -# {{classname}}{{#description}} -{{.}}{{/description}} - -All URIs are relative to *{{basePath}}* - -| Method | HTTP request | Description | -| ------------- | ------------- | ------------- | -{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} | -{{/operation}}{{/operations}} - -{{#operations}} -{{#operation}} - -# **{{operationId}}** -> {{#returnType}}{{.}} {{/returnType}}{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - -{{summary}}{{#notes}} - -{{.}}{{/notes}} - -### Example -```kotlin -// Import classes: -//import {{{packageName}}}.infrastructure.* -//import {{{modelPackage}}}.* - -{{! TODO: Auth method documentation examples}} -val apiInstance = {{{classname}}}() -{{#allParams}} -val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} -{{/allParams}} -try { - {{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}} - println(result){{/returnType}} -} catch (e: ClientException) { - println("4xx response calling {{{classname}}}#{{{operationId}}}") - e.printStackTrace() -} catch (e: ServerException) { - println("5xx response calling {{{classname}}}#{{{operationId}}}") - e.printStackTrace() -} -``` - -### Parameters -{{^allParams}} -This endpoint does not need any parameter. -{{/allParams}} -{{#allParams}} -{{#-last}} -| Name | Type | Description | Notes | -| ------------- | ------------- | ------------- | ------------- | -{{/-last}} -| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} | -{{/allParams}} - -### Return type - -{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} - -### Authorization - -{{^authMethods}}No authorization required{{/authMethods}} -{{#authMethods}} -{{#isApiKey}} -Configure {{name}}: - ApiClient.apiKey["{{keyParamName}}"] = "" - ApiClient.apiKeyPrefix["{{keyParamName}}"] = "" -{{/isApiKey}} -{{#isBasic}} -{{#isBasicBasic}} -Configure {{name}}: - ApiClient.username = "" - ApiClient.password = "" -{{/isBasicBasic}} -{{#isBasicBearer}} -Configure {{name}}: - ApiClient.accessToken = "" -{{/isBasicBearer}} -{{/isBasic}} -{{#isOAuth}} -Configure {{name}}: - ApiClient.accessToken = "" -{{/isOAuth}} -{{/authMethods}} - -### HTTP request headers - - - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} - - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} - -{{/operation}} -{{/operations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache deleted file mode 100644 index 78ae85a01d03..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/api_test.mustache +++ /dev/null @@ -1,33 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -import io.kotlintest.shouldBe -import io.kotlintest.specs.ShouldSpec - -import {{apiPackage}}.{{{classname}}} -{{#imports}}import {{import}} -{{/imports}} - -{{#operations}} -class {{{classname}}}Test : ShouldSpec() { - init { - // uncomment below to create an instance of {{{classname}}} - //val apiInstance = {{{classname}}}() - - {{#operation}} - // to test {{{operationId}}}{{#description}} - {{{.}}}{{/description}} - should("test {{{operationId}}}") { - // uncomment below to test {{{operationId}}} - {{#allParams}} - //val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} - {{/allParams}} - //{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - {{#returnType}} - //result shouldBe ("TODO") - {{/returnType}} - } - - {{/operation}} - } -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache deleted file mode 100644 index 1dfe6fd0636f..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/build.gradle.mustache +++ /dev/null @@ -1,216 +0,0 @@ -group '{{groupId}}' -version '{{artifactVersion}}' -{{^omitGradleWrapper}} - -wrapper { - gradleVersion = '8.7' - distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" -} -{{/omitGradleWrapper}} - -buildscript { - ext.kotlin_version = '1.9.23' - {{#jvm-ktor}} - ext.ktor_version = '2.3.9' - {{/jvm-ktor}} - {{#jvm-retrofit2}} - ext.retrofitVersion = '2.10.0' - {{/jvm-retrofit2}} - {{#useRxJava3}} - ext.rxJava3Version = '3.1.8' - {{/useRxJava3}} - {{#jvm-vertx}} - ext.vertx_version = "4.5.6" - {{/jvm-vertx}} - {{#jvm-spring}} - {{#useSpringBoot3}} - ext.spring_boot_version = "3.2.5" - {{/useSpringBoot3}} - {{^useSpringBoot3}} - ext.spring_boot_version = "2.7.18" - {{/useSpringBoot3}} - {{/jvm-spring}} - ext.spotless_version = "6.25.0" - - repositories { - maven { url "https://repo1.maven.org/maven2" } - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - {{#kotlinx_serialization}} - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - {{/kotlinx_serialization}} - classpath "com.diffplug.spotless:spotless-plugin-gradle:$spotless_version" - } -} - -apply plugin: 'kotlin' -{{#moshiCodeGen}} -apply plugin: 'kotlin-kapt' -{{/moshiCodeGen}} -{{#parcelizeModels}} -apply plugin: 'kotlin-parcelize' -{{/parcelizeModels}} -{{#kotlinx_serialization}} -apply plugin: 'kotlinx-serialization' -{{/kotlinx_serialization}} -{{#idea}} -apply plugin: 'idea' -{{/idea}} -apply plugin: 'maven-publish' -apply plugin: 'com.diffplug.spotless' -{{^useSettingsGradle}} - -repositories { - maven { url "https://repo1.maven.org/maven2" } -} -{{/useSettingsGradle}} - -// Use spotless plugin to automatically format code, remove unused import, etc -// To apply changes directly to the file, run `gradlew spotlessApply` -// Ref: https://github.com/diffplug/spotless/tree/main/plugin-gradle -spotless { - // comment out below to run spotless as part of the `check` task - enforceCheck false - - format 'misc', { - // define the files (e.g. '*.gradle', '*.md') to apply `misc` to - target '.gitignore' - - // define the steps to apply to those files - trimTrailingWhitespace() - indentWithSpaces() // Takes an integer argument if you don't like 4 - endWithNewline() - } - kotlin { - ktfmt() - } -} - -test { - useJUnitPlatform() -} -{{#idea}} - -idea { - module { - sourceDirs += file('src/main/kotlin') - testSourceDirs += file('src/test/kotlin') - } -} -{{/idea}} -{{#jvm-spring-webclient}}{{#useSpringBoot3}} -kotlin { - jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} -{{/useSpringBoot3}}{{/jvm-spring-webclient}} -{{#jvm-spring-restclient}} -kotlin { - jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} -{{/jvm-spring-restclient}} -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - {{^doNotUseRxAndCoroutines}} - {{#useCoroutines}} - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0" - {{/useCoroutines}} - {{/doNotUseRxAndCoroutines}} - {{#moshi}} - {{^moshiCodeGen}} - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - implementation "com.squareup.moshi:moshi-kotlin:1.15.1" - implementation "com.squareup.moshi:moshi-adapters:1.15.1" - {{/moshiCodeGen}} - {{#moshiCodeGen}} - implementation "com.squareup.moshi:moshi:1.15.1" - implementation "com.squareup.moshi:moshi-adapters:1.15.1" - kapt "com.squareup.moshi:moshi-kotlin-codegen:1.15.1" - {{/moshiCodeGen}} - {{/moshi}} - {{#gson}} - implementation "com.google.code.gson:gson:2.10.1" - {{/gson}} - {{#jackson}} - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.17.1" - implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1" - {{/jackson}} - {{#kotlinx_serialization}} - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3" - {{/kotlinx_serialization}} - {{#jvm-ktor}} - implementation "io.ktor:ktor-client-core:$ktor_version" - implementation "io.ktor:ktor-client-content-negotiation:$ktor_version" - {{#gson}} - implementation "io.ktor:ktor-serialization-gson:$ktor_version" - implementation "io.ktor:ktor-client-gson:$ktor_version" - {{/gson}} - {{#jackson}} - implementation "io.ktor:ktor-client-jackson:$ktor_version" - implementation "io.ktor:ktor-serialization-jackson:$ktor_version" - {{/jackson}} - {{/jvm-ktor}} - {{#jvm-okhttp4}} - implementation "com.squareup.okhttp3:okhttp:4.12.0" - {{/jvm-okhttp4}} - {{#jvm-spring-webclient}} - implementation "org.springframework.boot:spring-boot-starter-webflux:$spring_boot_version" - implementation "io.projectreactor:reactor-core:3.6.5" - {{/jvm-spring-webclient}} - {{#jvm-spring-restclient}} - implementation "org.springframework.boot:spring-boot-starter-web:$spring_boot_version" - {{/jvm-spring-restclient}} - {{#threetenbp}} - implementation "org.threeten:threetenbp:1.6.8" - {{/threetenbp}} - {{#kotlinx-datetime}} - implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.5.0" - {{/kotlinx-datetime}} - {{#jvm-retrofit2}} - {{#hasOAuthMethods}} - implementation "org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2" - {{/hasOAuthMethods}} - implementation "com.squareup.okhttp3:logging-interceptor:4.12.0" - {{#useRxJava3}} - implementation "io.reactivex.rxjava3:rxjava:$rxJava3Version" - implementation "com.squareup.retrofit2:adapter-rxjava3:2.10.0" - {{/useRxJava3}} - implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" - {{#gson}} - implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" - {{/gson}} - {{#moshi}} - implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion" - {{/moshi}} - {{#kotlinx_serialization}} - implementation "com.squareup.retrofit2:converter-kotlinx-serialization:$retrofitVersion" - {{/kotlinx_serialization}} - {{#jackson}} - implementation "com.squareup.retrofit2:converter-jackson:$retrofitVersion" - {{/jackson}} - implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion" - {{/jvm-retrofit2}} - testImplementation "io.kotlintest:kotlintest-runner-junit5:3.4.2" - {{#jvm-vertx}} - implementation "io.vertx:vertx-web-client:$vertx_version" - implementation "io.vertx:vertx-core:$vertx_version" - implementation "io.vertx:vertx-lang-kotlin:$vertx_version" - implementation "io.vertx:vertx-uri-template:$vertx_version" - {{#useCoroutines}} - implementation "io.vertx:vertx-lang-kotlin-coroutines:$vertx_version" - {{/useCoroutines}} - {{/jvm-vertx}} -} -{{#kotlinx_serialization}} - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { - kotlinOptions { - freeCompilerArgs += "-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi" - } -} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache deleted file mode 100644 index 6ab282918f35..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/class_doc.mustache +++ /dev/null @@ -1,15 +0,0 @@ -# {{classname}} - -## Properties -| Name | Type | Description | Notes | -| ------------ | ------------- | ------------- | ------------- | -{{#vars}}| **{{name}}** | {{#isEnum}}[**inline**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | -{{/vars}} -{{#vars}}{{#isEnum}} - -{{!NOTE: see java's resources "pojo_doc.mustache" once enums are fully implemented}} -## Enum: {{baseName}} -| Name | Value | -| ---- | ----- |{{#allowableValues}} -| {{name}} | {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}} | -{{/isEnum}}{{/vars}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache deleted file mode 100644 index 5f7c430d2661..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class.mustache +++ /dev/null @@ -1,377 +0,0 @@ -{{^multiplatform}} -{{#gson}} -{{#generateOneOfAnyOfWrappers}} -import com.google.gson.Gson -import com.google.gson.JsonElement -import com.google.gson.TypeAdapter -import com.google.gson.TypeAdapterFactory -import com.google.gson.reflect.TypeToken -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.annotations.JsonAdapter -import java.io.IOException -{{/generateOneOfAnyOfWrappers}} -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -{{/moshi}} -{{#jackson}} -{{#enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue -{{/enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonProperty -{{#discriminator}} -import com.fasterxml.jackson.annotation.JsonSubTypes -import com.fasterxml.jackson.annotation.JsonTypeInfo -{{/discriminator}} -{{/jackson}} -{{#kotlinx_serialization}} -import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} -import kotlinx.serialization.SerialName -import kotlinx.serialization.Contextual -import kotlinx.serialization.Polymorphic -import kotlinx.serialization.Transient -{{#enumUnknownDefaultCase}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -{{/enumUnknownDefaultCase}} -{{#hasEnums}} -{{/hasEnums}} -{{/kotlinx_serialization}} -{{#parcelizeModels}} -import android.os.Parcelable -import kotlinx.parcelize.Parcelize -{{/parcelizeModels}} -{{/multiplatform}} -{{#multiplatform}} -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* -{{/multiplatform}} -{{#serializableModel}} -import java.io.Serializable -{{/serializableModel}} -{{#generateRoomModels}} -import {{roomModelPackage}}.{{classname}}RoomModel -import {{packageName}}.infrastructure.ITransformForStorage -{{/generateRoomModels}} - -/** - * {{{description}}} - * -{{#allVars}} - * @param {{{name}}} {{{description}}} -{{/allVars}} - */ -{{#vendorExtensions.x-is-oneof-parent}} - {{#kotlinx_serialization}} -@Serializable -@Polymorphic -sealed interface {{classname}} - {{/kotlinx_serialization}} -{{/vendorExtensions.x-is-oneof-parent}} -{{^vendorExtensions.x-is-oneof-parent}} -{{#parcelizeModels}} -@Parcelize -{{/parcelizeModels}} - -{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} -{{#isDeprecated}} -@Deprecated(message = "This schema is deprecated.") -{{/isDeprecated}} -{{#vendorExtensions.x-has-serializable-type}} -@SerialName(value = "{{vendorExtensions.x-has-serializable-type}}") -{{/vendorExtensions.x-has-serializable-type}} -{{>additionalModelTypeAnnotations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}{{#discriminator}}interface{{/discriminator}}{{^discriminator}}data class{{/discriminator}} {{classname}}{{^discriminator}} ( - -{{#allVars}} - {{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}} -{{/allVars}} -){{/discriminator}}{{#vendorExtensions.x-has-oneof-parent}}: {{#allParents}}{{.}}{{^-last}}, {{/-last}}{{/allParents}}{{/vendorExtensions.x-has-oneof-parent}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} { -{{/vendorExtensions.x-has-data-class-body}} -{{#generateRoomModels}} - companion object { } - {{^discriminator}}override fun toRoomModel(): {{classname}}RoomModel = - {{classname}}RoomModel(roomTableId = 0, - {{#allVars}}{{#items.isPrimitiveType}}{{#isArray}}{{#isList}}{{name}} = this.{{name}},{{/isList}}{{/isArray}}{{/items.isPrimitiveType}}{{^isEnum}}{{^isArray}}{{name}} = this.{{name}},{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{name}} = this.{{name}},{{/isArray}}{{/isEnum}} - {{/allVars}} - ){{/discriminator}} -{{/generateRoomModels}} -{{#serializableModel}} - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private const val serialVersionUID: Long = 123 - } -{{/serializableModel}} -{{#discriminator}}{{#vars}}{{#required}} -{{>interface_req_var}}{{/required}}{{^required}} -{{>interface_opt_var}}{{/required}}{{/vars}}{{/discriminator}} -{{#hasEnums}} -{{#vars}} -{{#isEnum}} - /** - * {{{description}}} - * - * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} - */ - {{^multiplatform}} - {{#kotlinx_serialization}} - {{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{#enumUnknownDefaultCase}}(with = {{classname}}Serializer::class){{/enumUnknownDefaultCase}}{{/serializableModel}} - {{/kotlinx_serialization}} - {{#moshi}} - @JsonClass(generateAdapter = false) - {{/moshi}} - {{/multiplatform}} - {{#multiplatform}} - @Serializable - {{/multiplatform}} - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{{nameInPascalCase}}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{#allowableValues}} - {{#enumVars}} - {{^multiplatform}} - {{#moshi}} - @Json(name = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/moshi}} - {{#gson}} - @SerializedName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{#enumUnknownDefaultCase}}{{#-last}}@JsonEnumDefaultValue {{/-last}}{{/enumUnknownDefaultCase}}{{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}} - @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/multiplatform}} - {{/enumVars}} - {{/allowableValues}} - } - {{#kotlinx_serialization}} - {{#enumUnknownDefaultCase}} - - @Serializer(forClass = {{{nameInPascalCase}}}::class) - internal object {{nameInPascalCase}}Serializer : KSerializer<{{nameInPascalCase}}> { - override val descriptor = {{{dataType}}}.serializer().descriptor - - override fun deserialize(decoder: Decoder): {{nameInPascalCase}} { - val value = decoder.decodeSerializableValue({{{dataType}}}.serializer()) - return {{nameInPascalCase}}.values().firstOrNull { it.value == value } - ?: {{nameInPascalCase}}.{{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}} - } - - override fun serialize(encoder: Encoder, value: {{nameInPascalCase}}) { - encoder.encodeSerializableValue({{{dataType}}}.serializer(), value.value) - } - } - {{/enumUnknownDefaultCase}} - {{/kotlinx_serialization}} -{{/isEnum}} -{{/vars}} -{{/hasEnums}} -{{#generateOneOfAnyOfWrappers}} - {{#gson}} - - class CustomTypeAdapterFactory : TypeAdapterFactory { - override fun create(gson: Gson, type: TypeToken): TypeAdapter? { - if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { - return null // this class only serializes '{{classname}}' and its subtypes - } - val elementAdapter = gson.getAdapter(JsonElement::class.java) - val thisAdapter = gson.getDelegateAdapter(this, TypeToken.get({{classname}}::class.java)) - - @Suppress("UNCHECKED_CAST") - return object : TypeAdapter<{{classname}}>() { - @Throws(IOException::class) - override fun write(out: JsonWriter, value: {{classname}}) { - val obj = thisAdapter.toJsonTree(value).getAsJsonObject() - elementAdapter.write(out, obj) - } - - @Throws(IOException::class) - override fun read(jsonReader: JsonReader): {{classname}} { - val jsonElement = elementAdapter.read(jsonReader) - validateJsonElement(jsonElement) - return thisAdapter.fromJsonTree(jsonElement) - } - }.nullSafe() as TypeAdapter - } - } - - companion object { - var openapiFields = HashSet() - var openapiRequiredFields = HashSet() - - init { - {{#allVars}} - {{#-first}} - // a set of all properties/fields (JSON key names) - {{/-first}} - openapiFields.add("{{baseName}}") - {{/allVars}} - - {{#requiredVars}} - {{#-first}} - // a set of required properties/fields (JSON key names) - {{/-first}} - openapiRequiredFields.add("{{baseName}}") - {{/requiredVars}} - } - - /** - * Validates the JSON Element and throws an exception if issues found - * - * @param jsonElement JSON Element - * @throws IOException if the JSON Element is invalid with respect to {{classname}} - */ - @Throws(IOException::class) - fun validateJsonElement(jsonElement: JsonElement?) { - if (jsonElement == null) { - require(openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null - String.format("The required field(s) %s in {{{classname}}} is not found in the empty JSON string", {{classname}}.openapiRequiredFields.toString()) - } - } - {{^hasChildren}} - {{#requiredVars}} - {{#-first}} - - // check to make sure all required properties/fields are present in the JSON string - for (requiredField in openapiRequiredFields) { - requireNotNull(jsonElement!!.getAsJsonObject()[requiredField]) { - String.format("The required field `%s` is not found in the JSON string: %s", requiredField, jsonElement.toString()) - } - } - {{/-first}} - {{/requiredVars}} - {{/hasChildren}} - {{^discriminator}} - {{#hasVars}} - val jsonObj = jsonElement!!.getAsJsonObject() - {{/hasVars}} - {{#vars}} - {{#isArray}} - {{#items.isModel}} - {{#required}} - // ensure the json data is an array - if (!jsonObj.get("{{{baseName}}}").isJsonArray) { - throw IllegalArgumentException(String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString())) - } - - // validate the required field `{{{baseName}}}` (array) - for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) { - {{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i)) - } - {{/required}} - {{^required}} - if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { - if (jsonObj.getAsJsonArray("{{{baseName}}}") != null) { - // ensure the json data is an array - require(jsonObj["{{{baseName}}}"].isJsonArray) { - String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - - // validate the optional field `{{{baseName}}}` (array) - for (i in 0 until jsonObj.getAsJsonArray("{{{baseName}}}").size()) { - {{{items.dataType}}}.validateJsonElement(jsonObj.getAsJsonArray("{{{baseName}}}").get(i)) - } - } - } - {{/required}} - {{/items.isModel}} - {{^items.isModel}} - {{^required}} - // ensure the optional json data is an array if present - if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { - require(jsonObj["{{{baseName}}}"].isJsonArray()) { - String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - } - {{/required}} - {{#required}} - // ensure the required json array is present - requireNotNull(jsonObj["{{{baseName}}}"]) { - "Expected the field `{{{baseName}}}` to be an array in the JSON string but got `null`" - } - require(jsonObj["{{{baseName}}}"].isJsonArray()) { - String.format("Expected the field `{{{baseName}}}` to be an array in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - {{/required}} - {{/items.isModel}} - {{/isArray}} - {{^isContainer}} - {{#isString}} - {{#notRequiredOrIsNullable}} - if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { - require(jsonObj.get("{{{baseName}}}").isJsonPrimitive) { - String.format("Expected the field `{{{baseName}}}` to be a primitive type in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - } - {{/notRequiredOrIsNullable}} - {{^notRequiredOrIsNullable}} - require(jsonObj["{{{baseName}}}"].isJsonPrimitive) { - String.format("Expected the field `{{{baseName}}}` to be a primitive type in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - {{/notRequiredOrIsNullable}} - {{/isString}} - {{#isModel}} - {{#required}} - // validate the required field `{{{baseName}}}` - {{{dataType}}}.validateJsonElement(jsonObj["{{{baseName}}}"]) - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { - {{{dataType}}}.validateJsonElement(jsonObj["{{{baseName}}}"]) - } - {{/required}} - {{/isModel}} - {{#isEnum}} - {{#required}} - // validate the required field `{{{baseName}}}` - require({{{datatypeWithEnum}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { - String.format("Expected the field `{{{baseName}}}` to be valid `{{{datatypeWithEnum}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { - require({{{datatypeWithEnum}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { - String.format("Expected the field `{{{baseName}}}` to be valid `{{{datatypeWithEnum}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - } - {{/required}} - {{/isEnum}} - {{#isEnumRef}} - {{#required}} - // validate the required field `{{{baseName}}}` - require({{{dataType}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { - String.format("Expected the field `{{{baseName}}}` to be valid `{{{dataType}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - {{/required}} - {{^required}} - // validate the optional field `{{{baseName}}}` - if (jsonObj["{{{baseName}}}"] != null && !jsonObj["{{{baseName}}}"].isJsonNull) { - require({{{dataType}}}.values().any { it.value == jsonObj["{{{baseName}}}"].asString }) { - String.format("Expected the field `{{{baseName}}}` to be valid `{{{dataType}}}` enum value in the JSON string but got `%s`", jsonObj["{{{baseName}}}"].toString()) - } - } - {{/required}} - {{/isEnumRef}} - {{/isContainer}} - {{/vars}} - {{/discriminator}} - } - } - {{/gson}} -{{/generateOneOfAnyOfWrappers}} - -{{#vendorExtensions.x-has-data-class-body}} -} -{{/vendorExtensions.x-has-data-class-body}} -{{/vendorExtensions.x-is-oneof-parent}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache deleted file mode 100644 index 2c48810c1688..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_opt_var.mustache +++ /dev/null @@ -1,22 +0,0 @@ -{{#description}} - /* {{{.}}} */ -{{/description}} - {{^multiplatform}} - {{#moshi}} - @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") - {{/moshi}} - {{#gson}} - @SerializedName("{{{vendorExtensions.x-base-name-literal}}}") - {{/gson}} - {{#jackson}} - @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") - {{/jackson}} - {{#kotlinx_serialization}} - {{#vendorExtensions.x-is-transient}}@Transient{{/vendorExtensions.x-is-transient}} - {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#deprecated}} - @Deprecated(message = "This property is deprecated.") - {{/deprecated}} - {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? = {{^defaultValue}}null{{/defaultValue}}{{#defaultValue}}{{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache deleted file mode 100644 index 9193f72b4401..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/data_class_req_var.mustache +++ /dev/null @@ -1,22 +0,0 @@ -{{#description}} - /* {{{.}}} */ -{{/description}} - {{^multiplatform}} - {{#moshi}} - @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") - {{/moshi}} - {{#gson}} - @SerializedName("{{{vendorExtensions.x-base-name-literal}}}") - {{/gson}} - {{#jackson}} - @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") - {{/jackson}} - {{#kotlinx_serialization}} - {{#vendorExtensions.x-is-transient}}@Transient{{/vendorExtensions.x-is-transient}} - {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#deprecated}} - @Deprecated(message = "This property is deprecated.") - {{/deprecated}} - {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{#isInherited}}override {{/isInherited}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}{{#uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}Set{{/uniqueItems}}{{^uniqueItems}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/uniqueItems}}{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{^multiplatform}}{{{dataType}}}("{{{defaultValue}}}"){{/multiplatform}}{{#multiplatform}}({{{defaultValue}}}).toDouble(){{/multiplatform}}{{/isNumber}}{{/defaultValue}}{{#vendorExtensions.x-is-transient}} = ""{{/vendorExtensions.x-is-transient}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache deleted file mode 100644 index acbd45f92c7e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_class.mustache +++ /dev/null @@ -1,113 +0,0 @@ -{{^multiplatform}} -{{#gson}} -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -{{/moshi}} -{{#jackson}} -{{#enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue -{{/enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonProperty -{{/jackson}} -{{#kotlinx_serialization}} -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -{{#enumUnknownDefaultCase}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -{{/enumUnknownDefaultCase}} -{{/kotlinx_serialization}} -{{/multiplatform}} -{{#multiplatform}} -import kotlinx.serialization.* -{{/multiplatform}} - -/** - * {{{description}}} - * - * Values: {{#allowableValues}}{{#enumVars}}{{&name}}{{^-last}},{{/-last}}{{/enumVars}}{{/allowableValues}} - */ -{{#multiplatform}}@Serializable{{/multiplatform}}{{#kotlinx_serialization}}@Serializable{{#enumUnknownDefaultCase}}(with = {{classname}}Serializer::class){{/enumUnknownDefaultCase}}{{/kotlinx_serialization}} -{{^multiplatform}} -{{#moshi}} -@JsonClass(generateAdapter = false) -{{/moshi}} -{{/multiplatform}} -{{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{classname}}(val value: {{{dataType}}}) { -{{#allowableValues}}{{#enumVars}} - {{^multiplatform}} - {{#moshi}} - @Json(name = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) - {{/moshi}} - {{#gson}} - @SerializedName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}){{#enumUnknownDefaultCase}}{{#-last}} @JsonEnumDefaultValue{{/-last}}{{/enumUnknownDefaultCase}} - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}} - @SerialName(value = {{#lambda.doublequote}}{{{value}}}{{/lambda.doublequote}}) - {{/multiplatform}} - {{#isArray}} - {{#isList}} - {{&name}}(listOf({{{value}}})){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/isList}} - {{^isList}} - {{&name}}(arrayOf({{{value}}})){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/isList}} - {{/isArray}} - {{^isArray}} - {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/isArray}} -{{/enumVars}}{{/allowableValues}} - /** - * Override [toString()] to avoid using the enum variable name as the value, and instead use - * the actual value defined in the API spec file. - * - * This solves a problem when the variable name and its value are different, and ensures that - * the client sends the correct enum values to the server always. - */ - override fun toString(): kotlin.String = value{{^isString}}.toString(){{/isString}} - - companion object { - /** - * Converts the provided [data] to a [String] on success, null otherwise. - */ - fun encode(data: kotlin.Any?): kotlin.String? = if (data is {{classname}}) "$data" else null - - /** - * Returns a valid [{{classname}}] for [data], null otherwise. - */ - fun decode(data: kotlin.Any?): {{classname}}? = data?.let { - val normalizedData = "$it".lowercase() - values().firstOrNull { value -> - it == value || normalizedData == "$value".lowercase() - } - } - } -}{{#kotlinx_serialization}}{{#enumUnknownDefaultCase}} - -@Serializer(forClass = {{classname}}::class) -internal object {{classname}}Serializer : KSerializer<{{classname}}> { - override val descriptor = {{{dataType}}}.serializer().descriptor - - override fun deserialize(decoder: Decoder): {{classname}} { - val value = decoder.decodeSerializableValue({{{dataType}}}.serializer()) - return {{classname}}.values().firstOrNull { it.value == value } - ?: {{classname}}.{{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}} - } - - override fun serialize(encoder: Encoder, value: {{classname}}) { - encoder.encodeSerializableValue({{{dataType}}}.serializer(), value.value) - } -}{{/enumUnknownDefaultCase}}{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache deleted file mode 100644 index fcb3d7e61aa6..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/enum_doc.mustache +++ /dev/null @@ -1,7 +0,0 @@ -# {{classname}} - -## Enum - -{{#allowableValues}}{{#enumVars}} - * `{{name}}` (value: `{{{value}}}`) -{{/enumVars}}{{/allowableValues}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.jar b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.jar deleted file mode 100644 index d64cd4917707c1f8861d8cb53dd15194d4248596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43462 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 diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache deleted file mode 100644 index 6eb2a6bb7b23..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradle-wrapper.properties.mustache +++ /dev/null @@ -1,12 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -{{#jvm-volley}} -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-all.zip -{{/jvm-volley}} -{{^jvm-volley}} -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip -{{/jvm-volley}} -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache deleted file mode 100644 index 107acd32c4e6..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.bat.mustache +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache deleted file mode 100755 index 9d0ce634cb11..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/gradlew.mustache +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while -APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path -[ -h "$app_path" ] -do -ls=$( ls -ld "$app_path" ) -link=${ls#*' -> '} -case $link in #( -/*) app_path=$link ;; #( -*) app_path=$APP_HOME$link ;; -esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { -echo "$*" -} >&2 - -die () { -echo -echo "$*" -echo -exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( -CYGWIN* ) cygwin=true ;; #( -Darwin* ) darwin=true ;; #( -MSYS* | MINGW* ) msys=true ;; #( -NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then -if [ -x "$JAVA_HOME/jre/sh/java" ] ; then -# IBM's JDK on AIX uses strange locations for the executables -JAVACMD=$JAVA_HOME/jre/sh/java -else -JAVACMD=$JAVA_HOME/bin/java -fi -if [ ! -x "$JAVACMD" ] ; then -die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi -else -JAVACMD=java -if ! command -v java >/dev/null 2>&1 -then -die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then -case $MAX_FD in #( -max*) -# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. -# shellcheck disable=SC2039,SC3045 -MAX_FD=$( ulimit -H -n ) || -warn "Could not query maximum file descriptor limit" -esac -case $MAX_FD in #( -'' | soft) :;; #( -*) -# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. -# shellcheck disable=SC2039,SC3045 -ulimit -n "$MAX_FD" || -warn "Could not set maximum file descriptor limit to $MAX_FD" -esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then -APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) -CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - -JAVACMD=$( cygpath --unix "$JAVACMD" ) - -# Now convert the arguments - kludge to limit ourselves to /bin/sh -for arg do -if -case $arg in #( --*) false ;; # don't mess with options #( -/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath -[ -e "$t" ] ;; #( -*) false ;; -esac -then -arg=$( cygpath --path --ignore --mixed "$arg" ) -fi -# Roll the args list around exactly as many times as the number of -# args, so each arg winds up back in the position where it started, but -# possibly modified. -# -# NB: a `for` loop captures its iteration list before it begins, so -# changing the positional parameters here affects neither the number of -# iterations, nor the values presented in `arg`. -shift # remove old arg -set -- "$@" "$arg" # push replacement arg -done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ -"-Dorg.gradle.appname=$APP_BASE_NAME" \ --classpath "$CLASSPATH" \ -org.gradle.wrapper.GradleWrapperMain \ -"$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then -die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( -printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | -xargs -n1 | -sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | -tr '\n' ' ' -)" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache deleted file mode 100644 index 523503eef9d0..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/ApiAbstractions.kt.mustache +++ /dev/null @@ -1,23 +0,0 @@ -package {{packageName}}.infrastructure - -{{#nonPublicApi}}internal {{/nonPublicApi}}typealias MultiValueMap = MutableMap> - -{{#nonPublicApi}}internal {{/nonPublicApi}}fun collectionDelimiter(collectionFormat: String) = when(collectionFormat) { - "csv" -> "," - "tsv" -> "\t" - "pipe" -> "|" - "space" -> " " - else -> "" -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}val defaultMultiValueConverter: (item: Any?) -> String = { item -> "$item" } - -{{#nonPublicApi}}internal {{/nonPublicApi}}fun toMultiValue(items: Array, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter) - = toMultiValue(items.asIterable(), collectionFormat, map) - -{{#nonPublicApi}}internal {{/nonPublicApi}}fun toMultiValue(items: Iterable, collectionFormat: String, map: (item: T) -> String = defaultMultiValueConverter): List { - return when(collectionFormat) { - "multi" -> items.map(map) - else -> listOf(items.joinToString(separator = collectionDelimiter(collectionFormat), transform = map)) - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache deleted file mode 100644 index f70c18eff465..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/PartConfig.kt.mustache +++ /dev/null @@ -1,11 +0,0 @@ -package {{packageName}}.infrastructure - -/** - * Defines a config object for a given part of a multi-part request. - * NOTE: Headers is a Map because rfc2616 defines - * multi-valued headers as csv-only. - */ -{{#nonPublicApi}}internal {{/nonPublicApi}}data class PartConfig( - val headers: MutableMap = mutableMapOf(), - val body: T? = null -) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache deleted file mode 100644 index 7de4c68ae02e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestConfig.kt.mustache +++ /dev/null @@ -1,19 +0,0 @@ -package {{packageName}}.infrastructure - -/** - * Defines a config object for a given request. - * 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. - */ -{{#nonPublicApi}}internal {{/nonPublicApi}}data class RequestConfig( - val method: RequestMethod, - val path: String, - val headers: MutableMap = mutableMapOf(), - val params: MutableMap = mutableMapOf(), - val query: MutableMap> = mutableMapOf(), - val requiresAuthentication: Boolean, - val body: T? = null -) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache deleted file mode 100644 index cb59c4f58c8c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/infrastructure/RequestMethod.kt.mustache +++ /dev/null @@ -1,8 +0,0 @@ -package {{packageName}}.infrastructure - -/** - * Provides enumerated HTTP verbs - */ -{{#nonPublicApi}}internal {{/nonPublicApi}}enum class RequestMethod { - GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache deleted file mode 100644 index 260be5c3caa8..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_opt_var.mustache +++ /dev/null @@ -1,18 +0,0 @@ -{{#description}} - /* {{{.}}} */ -{{/description}} - {{^multiplatform}} - {{#moshi}} - @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") - {{/moshi}} - {{#gson}} - @get:SerializedName("{{{vendorExtensions.x-base-name-literal}}}") - {{/gson}} - {{#jackson}} - @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") - {{/jackson}} - {{#kotlinx_serialization}} - {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{/multiplatform}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}? \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache deleted file mode 100644 index 6560b23d9626..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/interface_req_var.mustache +++ /dev/null @@ -1,18 +0,0 @@ -{{#description}} - /* {{{.}}} */ -{{/description}} - {{^multiplatform}} - {{#moshi}} - @Json(name = "{{{vendorExtensions.x-base-name-literal}}}") - {{/moshi}} - {{#gson}} - @get:SerializedName("{{{vendorExtensions.x-base-name-literal}}}") - {{/gson}} - {{#jackson}} - @get:JsonProperty("{{{vendorExtensions.x-base-name-literal}}}") - {{/jackson}} - {{#kotlinx_serialization}} - {{^isEnum}}{{^isArray}}{{^isPrimitiveType}}{{^isModel}}@Contextual {{/isModel}}{{/isPrimitiveType}}{{/isArray}}{{/isEnum}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}}@SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") @Required {{/multiplatform}}{{>modelMutable}} {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.{{#modelMutable}}Mutable{{/modelMutable}}List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{#kotlinx_serialization}}@Contextual {{/kotlinx_serialization}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{/isArray}}{{^isEnum}}{{^isArray}}{{{dataType}}}{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}.{{{nameInPascalCase}}}{{/isArray}}{{/isEnum}}{{#isNullable}}?{{/isNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache deleted file mode 100644 index 098ff54c6a1c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicBooleanAdapter.kt.mustache +++ /dev/null @@ -1,21 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -import java.util.concurrent.atomic.AtomicBoolean - -@Serializer(forClass = AtomicBoolean::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object AtomicBooleanAdapter : KSerializer { - override fun serialize(encoder: Encoder, value: AtomicBoolean) { - encoder.encodeBoolean(value.get()) - } - - override fun deserialize(decoder: Decoder): AtomicBoolean = AtomicBoolean(decoder.decodeBoolean()) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicBoolean", PrimitiveKind.BOOLEAN) -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache deleted file mode 100644 index 6e16b5b78a6d..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicIntegerAdapter.kt.mustache +++ /dev/null @@ -1,21 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -import java.util.concurrent.atomic.AtomicInteger - -@Serializer(forClass = AtomicInteger::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object AtomicIntegerAdapter : KSerializer { - override fun serialize(encoder: Encoder, value: AtomicInteger) { - encoder.encodeInt(value.get()) - } - - override fun deserialize(decoder: Decoder): AtomicInteger = AtomicInteger(decoder.decodeInt()) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicInteger", PrimitiveKind.INT) -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache deleted file mode 100644 index bc2142c676a7..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/AtomicLongAdapter.kt.mustache +++ /dev/null @@ -1,21 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -import java.util.concurrent.atomic.AtomicLong - -@Serializer(forClass = AtomicLong::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object AtomicLongAdapter : KSerializer { - override fun serialize(encoder: Encoder, value: AtomicLong) { - encoder.encodeLong(value.get()) - } - - override fun deserialize(decoder: Decoder): AtomicLong = AtomicLong(decoder.decodeLong()) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("AtomicLong", PrimitiveKind.LONG) -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache deleted file mode 100644 index fd08ed4a04cc..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigDecimalAdapter.kt.mustache +++ /dev/null @@ -1,38 +0,0 @@ -package {{packageName}}.infrastructure - -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -import java.math.BigDecimal - -{{#kotlinx_serialization}} -@Serializer(forClass = BigDecimal::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object BigDecimalAdapter : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): BigDecimal = BigDecimal(decoder.decodeString()) - override fun serialize(encoder: Encoder, value: BigDecimal) = encoder.encodeString(value.toPlainString()) -} -{{/kotlinx_serialization}} -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class BigDecimalAdapter { - @ToJson - fun toJson(value: BigDecimal): String { - return value.toPlainString() - } - - @FromJson - fun fromJson(value: String): BigDecimal { - return BigDecimal(value) - } -} -{{/moshi}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache deleted file mode 100644 index e7403a6882af..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/BigIntegerAdapter.kt.mustache +++ /dev/null @@ -1,43 +0,0 @@ -package {{packageName}}.infrastructure - -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -import java.math.BigInteger - -{{#kotlinx_serialization}} -@Serializer(forClass = BigInteger::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object BigIntegerAdapter : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigInteger", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): BigInteger { - return BigInteger(decoder.decodeString()) - } - - override fun serialize(encoder: Encoder, value: BigInteger) { - encoder.encodeString(value.toString()) - } -} -{{/kotlinx_serialization}} -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class BigIntegerAdapter { - @ToJson - fun toJson(value: BigInteger): String { - return value.toString() - } - - @FromJson - fun fromJson(value: String): BigInteger { - return BigInteger(value) - } -} -{{/moshi}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache deleted file mode 100644 index edc967afdbc2..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/ByteArrayAdapter.kt.mustache +++ /dev/null @@ -1,50 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -{{#gson}} -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.stream.JsonToken.NULL -import java.io.IOException -{{/gson}} - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class ByteArrayAdapter { - @ToJson - fun toJson(data: ByteArray): String = String(data) - - @FromJson - fun fromJson(data: String): ByteArray = data.toByteArray() -} -{{/moshi}} -{{#gson}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class ByteArrayAdapter : TypeAdapter() { - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: ByteArray?) { - if (value == null) { - out?.nullValue() - } else { - out?.value(String(value)) - } - } - - @Throws(IOException::class) - override fun read(out: JsonReader?): ByteArray? { - out ?: return null - - when (out.peek()) { - NULL -> { - out.nextNull() - return null - } - else -> { - return out.nextString().toByteArray() - } - } - } -} -{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache deleted file mode 100644 index 66d389c3f6d0..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/InstantAdapter.kt.mustache +++ /dev/null @@ -1,56 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -{{#gson}} -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.stream.JsonToken.NULL -import java.io.IOException -{{/gson}} -import kotlinx.datetime.Instant - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class InstantAdapter { - @ToJson - fun toJson(value: Instant): String { - return value.toString() - } - - @FromJson - fun fromJson(value: String): Instant { - return Instant.parse(value) - } - -} -{{/moshi}} -{{#gson}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class InstantAdapter : TypeAdapter() { - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: Instant?) { - if (value == null) { - out?.nullValue() - } else { - out?.value(value.toString()) - } - } - - @Throws(IOException::class) - override fun read(out: JsonReader?): Instant? { - out ?: return null - - when (out.peek()) { - NULL -> { - out.nextNull() - return null - } - else -> { - return Instant.parse(out.nextString()) - } - } - } -} -{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache deleted file mode 100644 index 6809c74c3a77..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateAdapter.kt.mustache +++ /dev/null @@ -1,103 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -{{#gson}} -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.stream.JsonToken.NULL -import java.io.IOException -{{/gson}} -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{^threetenbp}} -{{^kotlinx-datetime}} -import java.time.LocalDate -import java.time.format.DateTimeFormatter -{{/kotlinx-datetime}} -{{/threetenbp}} -{{#threetenbp}} -import org.threeten.bp.LocalDate -import org.threeten.bp.format.DateTimeFormatter -{{/threetenbp}} -{{#kotlinx-datetime}} -import kotlinx.datetime.LocalDate -{{/kotlinx-datetime}} - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateAdapter { - @ToJson - fun toJson(value: LocalDate): String { - {{#kotlinx-datetime}} - return value.toString() - {{/kotlinx-datetime}} - {{^kotlinx-datetime}} - return DateTimeFormatter.ISO_LOCAL_DATE.format(value) - {{/kotlinx-datetime}} - } - - @FromJson - fun fromJson(value: String): LocalDate { - return LocalDate.parse(value{{^kotlinx-datetime}}, DateTimeFormatter.ISO_LOCAL_DATE{{/kotlinx-datetime}}) - } - -} -{{/moshi}} -{{#gson}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateAdapter({{^kotlinx-datetime}}private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE{{/kotlinx-datetime}}) : TypeAdapter() { - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: LocalDate?) { - if (value == null) { - out?.nullValue() - } else { - {{#kotlinx-datetime}} - out?.value(value.toString()) - {{/kotlinx-datetime}} - {{^kotlinx-datetime}} - out?.value(formatter.format(value)) - {{/kotlinx-datetime}} - } - } - - @Throws(IOException::class) - override fun read(out: JsonReader?): LocalDate? { - out ?: return null - - when (out.peek()) { - NULL -> { - out.nextNull() - return null - } - else -> { - return LocalDate.parse(out.nextString(){{^kotlinx-datetime}}, formatter{{/kotlinx-datetime}}) - } - } - } -} -{{/gson}} -{{#kotlinx_serialization}} -{{^kotlinx-datetime}} -@Serializer(forClass = LocalDate::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object LocalDateAdapter : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING) - - override fun serialize(encoder: Encoder, value: LocalDate) { - encoder.encodeString(DateTimeFormatter.ISO_LOCAL_DATE.format(value)) - } - - override fun deserialize(decoder: Decoder): LocalDate { - return LocalDate.parse(decoder.decodeString(), DateTimeFormatter.ISO_LOCAL_DATE) - } -} -{{/kotlinx-datetime}} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache deleted file mode 100644 index f56cf12d5b77..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalDateTimeAdapter.kt.mustache +++ /dev/null @@ -1,103 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -{{#gson}} -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.stream.JsonToken.NULL -import java.io.IOException -{{/gson}} -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{^threetenbp}} -{{^kotlinx-datetime}} -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -{{/kotlinx-datetime}} -{{/threetenbp}} -{{#threetenbp}} -import org.threeten.bp.LocalDateTime -import org.threeten.bp.format.DateTimeFormatter -{{/threetenbp}} -{{#kotlinx-datetime}} -import kotlinx.datetime.LocalDateTime -{{/kotlinx-datetime}} - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateTimeAdapter { - @ToJson - fun toJson(value: LocalDateTime): String { - {{#kotlinx-datetime}} - return value.toString() - {{/kotlinx-datetime}} - {{^kotlinx-datetime}} - return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value) - {{/kotlinx-datetime}} - } - - @FromJson - fun fromJson(value: String): LocalDateTime { - return LocalDateTime.parse(value{{^kotlinx-datetime}}, DateTimeFormatter.ISO_LOCAL_DATE_TIME{{/kotlinx-datetime}}) - } - -} -{{/moshi}} -{{#gson}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalDateTimeAdapter({{^kotlinx-datetime}}private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME{{/kotlinx-datetime}}) : TypeAdapter() { - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: LocalDateTime?) { - if (value == null) { - out?.nullValue() - } else { - {{#kotlinx-datetime}} - out?.value(value.toString()) - {{/kotlinx-datetime}} - {{^kotlinx-datetime}} - out?.value(formatter.format(value)) - {{/kotlinx-datetime}} - } - } - - @Throws(IOException::class) - override fun read(out: JsonReader?): LocalDateTime? { - out ?: return null - - when (out.peek()) { - NULL -> { - out.nextNull() - return null - } - else -> { - return LocalDateTime.parse(out.nextString(){{^kotlinx-datetime}}, formatter{{/kotlinx-datetime}}) - } - } - } -} -{{/gson}} -{{#kotlinx_serialization}} -{{^kotlinx-datetime}} -@Serializer(forClass = LocalDateTime::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object LocalDateTimeAdapter : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING) - - override fun serialize(encoder: Encoder, value: LocalDateTime) { - encoder.encodeString(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value)) - } - - override fun deserialize(decoder: Decoder): LocalDateTime { - return LocalDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME) - } -} -{{/kotlinx-datetime}} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache deleted file mode 100644 index 3a294f8ef7df..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/LocalTimeAdapter.kt.mustache +++ /dev/null @@ -1,56 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -{{#gson}} -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.stream.JsonToken.NULL -import java.io.IOException -{{/gson}} -import kotlinx.datetime.LocalTime - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalTimeAdapter { - @ToJson - fun toJson(value: LocalTime): String { - return value.toString() - } - - @FromJson - fun fromJson(value: String): LocalTime { - return LocalTime.parse(value) - } - -} -{{/moshi}} -{{#gson}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class LocalTimeAdapter : TypeAdapter() { - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: LocalTime?) { - if (value == null) { - out?.nullValue() - } else { - out?.value(value.toString()) - } - } - - @Throws(IOException::class) - override fun read(out: JsonReader?): LocalTime? { - out ?: return null - - when (out.peek()) { - NULL -> { - out.nextNull() - return null - } - else -> { - return LocalTime.parse(out.nextString()) - } - } - } -} -{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache deleted file mode 100644 index b2d47121654b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/OffsetDateTimeAdapter.kt.mustache +++ /dev/null @@ -1,86 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -{{#gson}} -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.stream.JsonToken.NULL -import java.io.IOException -{{/gson}} -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{^threetenbp}} -import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter -{{/threetenbp}} -{{#threetenbp}} -import org.threeten.bp.OffsetDateTime -import org.threeten.bp.format.DateTimeFormatter -{{/threetenbp}} - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class OffsetDateTimeAdapter { - @ToJson - fun toJson(value: OffsetDateTime): String { - return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value) - } - - @FromJson - fun fromJson(value: String): OffsetDateTime { - return OffsetDateTime.parse(value, DateTimeFormatter.ISO_OFFSET_DATE_TIME) - } - -} -{{/moshi}} -{{#gson}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class OffsetDateTimeAdapter(private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME) : TypeAdapter() { - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: OffsetDateTime?) { - if (value == null) { - out?.nullValue() - } else { - out?.value(formatter.format(value)) - } - } - - @Throws(IOException::class) - override fun read(out: JsonReader?): OffsetDateTime? { - out ?: return null - - when (out.peek()) { - NULL -> { - out.nextNull() - return null - } - else -> { - return OffsetDateTime.parse(out.nextString(), formatter) - } - } - } -} -{{/gson}} -{{#kotlinx_serialization}} -@Serializer(forClass = OffsetDateTime::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object OffsetDateTimeAdapter : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("OffsetDateTime", PrimitiveKind.STRING) - - override fun serialize(encoder: Encoder, value: OffsetDateTime) { - encoder.encodeString(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value)) - } - - override fun deserialize(decoder: Decoder): OffsetDateTime { - return OffsetDateTime.parse(decoder.decodeString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME) - } -} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache deleted file mode 100644 index 1c02bac73feb..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/Serializer.kt.mustache +++ /dev/null @@ -1,168 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.Moshi -{{#enumUnknownDefaultCase}} -import com.squareup.moshi.adapters.EnumJsonAdapter -{{/enumUnknownDefaultCase}} -{{^moshiCodeGen}} -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -{{/moshiCodeGen}} -{{/moshi}} -{{#gson}} -import com.google.gson.Gson -import com.google.gson.GsonBuilder -{{^threetenbp}} -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.OffsetDateTime -{{/threetenbp}} -{{#threetenbp}} -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.OffsetDateTime -{{/threetenbp}} -{{#kotlinx-datetime}} -import kotlinx.datetime.Instant -import kotlinx.datetime.LocalTime -{{/kotlinx-datetime}} -import java.util.UUID -{{/gson}} -{{#jackson}} -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.SerializationFeature -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -{{/jackson}} -{{#kotlinx_serialization}} -import java.math.BigDecimal -import java.math.BigInteger -{{^threetenbp}} -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.OffsetDateTime -{{/threetenbp}} -{{#threetenbp}} -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.OffsetDateTime -{{/threetenbp}} -import java.util.UUID -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic -{{#models}} - {{#model}} - {{#oneOf.size}} -import {{packageName}}.models.{{classname}} -{{#oneOf}} -import {{packageName}}.models.{{.}} -{{/oneOf}} -{{/oneOf.size}} -{{/model}} -{{/models}} -import java.net.URI -import java.net.URL -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong -{{/kotlinx_serialization}} - -{{#nonPublicApi}}internal {{/nonPublicApi}}object Serializer { -{{#moshi}} - @JvmStatic - val moshiBuilder: Moshi.Builder = Moshi.Builder() - .add(OffsetDateTimeAdapter()) - {{#kotlinx-datetime}} - .add(InstantAdapter()) - .add(LocalTimeAdapter()) - {{/kotlinx-datetime}} - .add(LocalDateTimeAdapter()) - .add(LocalDateAdapter()) - .add(UUIDAdapter()) - .add(ByteArrayAdapter()) - .add(URIAdapter()) - {{^moshiCodeGen}} - .add(KotlinJsonAdapterFactory()) - {{/moshiCodeGen}} - .add(BigDecimalAdapter()) - .add(BigIntegerAdapter()) - - @JvmStatic - val moshi: Moshi by lazy { -{{#enumUnknownDefaultCase}} - SerializerHelper.addEnumUnknownDefaultCase(moshiBuilder) -{{/enumUnknownDefaultCase}} - moshiBuilder.build() - } -{{/moshi}} -{{#gson}} - @JvmStatic - val gsonBuilder: GsonBuilder = GsonBuilder() - .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) - {{#kotlinx-datetime}} - .registerTypeAdapter(Instant::class.java, InstantAdapter()) - .registerTypeAdapter(LocalTime::class.java, LocalTimeAdapter()) - {{/kotlinx-datetime}} - .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter()) - .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) - .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) - - @JvmStatic - val gson: Gson by lazy { - gsonBuilder.create() - } -{{/gson}} -{{#jackson}} - @JvmStatic - val jacksonObjectMapper: ObjectMapper = jacksonObjectMapper() - .findAndRegisterModules() - .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) - {{#enumUnknownDefaultCase}} - .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true) - {{/enumUnknownDefaultCase}} - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, {{failOnUnknownProperties}}) -{{/jackson}} -{{#kotlinx_serialization}} - @Deprecated("Use Serializer.kotlinxSerializationAdapters instead", replaceWith = ReplaceWith("Serializer.kotlinxSerializationAdapters")) - @JvmStatic - val kotlinSerializationAdapters: SerializersModule - get() { return kotlinxSerializationAdapters } - - val oneOfSerializers = SerializersModule { - {{#models}} - {{#model}} - {{#oneOf.size}} - polymorphic({{classname}}Resource::class) { - {{#oneOf}} - subclass({{.}}::class, {{.}}.serializer()) - {{/oneOf}} - } - {{/oneOf.size}} - {{/model}} - {{/models}} - } - - @JvmStatic - val kotlinxSerializationAdapters = SerializersModule { - {{#models.oneOf.size}}include(oneOfSerializers){{/models.oneOf.size}} - } - - @Deprecated("Use Serializer.kotlinxSerializationJson instead", replaceWith = ReplaceWith("Serializer.kotlinxSerializationJson")) - @JvmStatic - val jvmJson: Json - get() { return kotlinxSerializationJson } - - @JvmStatic - val kotlinxSerializationJson: Json by lazy { - Json { - serializersModule = kotlinxSerializationAdapters - encodeDefaults = true - ignoreUnknownKeys = true - isLenient = true - } - } -{{/kotlinx_serialization}} -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache deleted file mode 100644 index 27f4d1ee617c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/SerializerHelper.kt.mustache +++ /dev/null @@ -1,50 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.Moshi -{{#enumUnknownDefaultCase}} -import com.squareup.moshi.adapters.EnumJsonAdapter -{{/enumUnknownDefaultCase}} -{{/moshi}} - -{{#nonPublicApi}}internal {{/nonPublicApi}}object SerializerHelper { -{{#moshi}} - fun addEnumUnknownDefaultCase(moshiBuilder: Moshi.Builder): Moshi.Builder { - return moshiBuilder -{{#enumUnknownDefaultCase}} -{{#models}} -{{#model}} -{{#isEnum}} -{{#allowableValues}} -{{#enumVars}} -{{#-last}} - .add({{modelPackage}}.{{classname}}::class.java, EnumJsonAdapter.create({{modelPackage}}.{{classname}}::class.java) - .withUnknownFallback({{modelPackage}}.{{classname}}.{{&name}})) -{{/-last}} -{{/enumVars}} -{{/allowableValues}} -{{/isEnum}} -{{^isEnum}} -{{^isAlias}} -{{#hasEnums}} -{{#vars}} -{{#isEnum}} -{{#allowableValues}} -{{#enumVars}} -{{#-last}} - .add({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}::class.java, EnumJsonAdapter.create({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}::class.java) - .withUnknownFallback({{modelPackage}}.{{classname}}.{{{nameInPascalCase}}}.{{&name}})) -{{/-last}} -{{/enumVars}} -{{/allowableValues}} -{{/isEnum}} -{{/vars}} -{{/hasEnums}} -{{/isAlias}} -{{/isEnum}} -{{/model}} -{{/models}} -{{/enumUnknownDefaultCase}} - } -{{/moshi}} -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache deleted file mode 100644 index 3d5367030716..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/StringBuilderAdapter.kt.mustache +++ /dev/null @@ -1,20 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor - -@Serializer(forClass = StringBuilder::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object StringBuilderAdapter : KSerializer { - override fun serialize(encoder: Encoder, value: StringBuilder) { - encoder.encodeString(value.toString()) - } - - override fun deserialize(decoder: Decoder): StringBuilder = StringBuilder(decoder.decodeString()) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("StringBuilder", PrimitiveKind.STRING) -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache deleted file mode 100644 index 563b39cdb9a9..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URIAdapter.kt.mustache +++ /dev/null @@ -1,38 +0,0 @@ -package {{packageName}}.infrastructure - -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -import java.net.URI - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class URIAdapter { - @ToJson - fun toJson(uri: URI) = uri.toString() - - @FromJson - fun fromJson(s: String): URI = URI.create(s) -} -{{/moshi}} -{{#kotlinx_serialization}} -@Serializer(forClass = URI::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object URIAdapter : KSerializer { - override fun serialize(encoder: Encoder, value: URI) { - encoder.encodeString(value.toASCIIString()) - } - - override fun deserialize(decoder: Decoder): URI = URI(decoder.decodeString()) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URI", PrimitiveKind.STRING) -} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache deleted file mode 100644 index b437ff89ae0e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/URLAdapter.kt.mustache +++ /dev/null @@ -1,21 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -import java.net.URL - -@Serializer(forClass = URL::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object URLAdapter : KSerializer { - override fun serialize(encoder: Encoder, value: URL) { - encoder.encodeString(value.toExternalForm()) - } - - override fun deserialize(decoder: Decoder): URL = URL(decoder.decodeString()) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("URL", PrimitiveKind.STRING) -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache deleted file mode 100644 index 925532d6aa3f..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/UUIDAdapter.kt.mustache +++ /dev/null @@ -1,40 +0,0 @@ -package {{packageName}}.infrastructure - -{{#kotlinx_serialization}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.SerialDescriptor -{{/kotlinx_serialization}} -{{#moshi}} -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -{{/moshi}} -import java.util.UUID - -{{#moshi}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class UUIDAdapter { - @ToJson - fun toJson(uuid: UUID) = uuid.toString() - - @FromJson - fun fromJson(s: String): UUID = UUID.fromString(s) -} -{{/moshi}} -{{#kotlinx_serialization}} -@Serializer(forClass = UUID::class) -{{#nonPublicApi}}internal {{/nonPublicApi}}object UUIDAdapter : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING) - - override fun serialize(encoder: Encoder, value: UUID) { - encoder.encodeString(value.toString()) - } - - override fun deserialize(decoder: Decoder): UUID { - return UUID.fromString(decoder.decodeString()) - } -} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache deleted file mode 100644 index 6da4eba50603..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/jvm-common/infrastructure/proguard-rules.pro.mustache +++ /dev/null @@ -1,11 +0,0 @@ --keepattributes *Annotation*, InnerClasses --dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations - -# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer --keepclassmembers class kotlinx.serialization.json.** { *** Companion; } --keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); } - -# project specific. --keep,includedescriptorclasses class {{modelPackage}}.**$$serializer { *; } --keepclassmembers class {{modelPackage}}.** { *** Companion; } --keepclasseswithmembers class {{modelPackage}}.** { kotlinx.serialization.KSerializer serializer(...); } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache deleted file mode 100644 index c6ca0ce0c06d..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/api.mustache +++ /dev/null @@ -1,152 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -{{#imports}}import {{import}} -{{/imports}} - -import {{packageName}}.infrastructure.* -import io.ktor.client.HttpClientConfig -import io.ktor.client.request.forms.formData -import io.ktor.client.engine.HttpClientEngine -import io.ktor.http.ParametersBuilder -{{#gson}} -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import java.text.DateFormat -{{/gson}} -{{#jackson}} -import com.fasterxml.jackson.databind.ObjectMapper -{{/jackson}} - -{{#operations}} - {{#nonPublicApi}}internal {{/nonPublicApi}}open class {{classname}}( - baseUrl: String = ApiClient.BASE_URL, - httpClientEngine: HttpClientEngine? = null, - httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, - {{#gson}} - jsonBlock: GsonBuilder.() -> Unit = ApiClient.JSON_DEFAULT, - {{/gson}} - {{#jackson}} - jsonBlock: ObjectMapper.() -> Unit = ApiClient.JSON_DEFAULT, - {{/jackson}} - ) : ApiClient( - baseUrl, - httpClientEngine, - httpClientConfig, - {{#gson}} - jsonBlock, - {{/gson}} - {{#jackson}} - jsonBlock, - {{/jackson}} - ) { - - {{#operation}} - /** - * {{summary}} - * {{notes}} - {{#allParams}} * @param {{{paramName}}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} - {{/allParams}} * @return {{{returnType}}}{{^returnType}}void{{/returnType}} - */ - {{#returnType}} - @Suppress("UNCHECKED_CAST") - {{/returnType}} - open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}> { - - val localVariableAuthNames = listOf({{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}}) - - val localVariableBody = {{#hasBodyParam}}{{#bodyParam}}{{{paramName}}}{{/bodyParam}}{{/hasBodyParam}} - {{^hasBodyParam}} - {{#hasFormParams}} - {{#isMultipart}} - formData { - {{#formParams}} - {{#isFile}} - {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } - {{/isFile}} - {{^isFile}} - {{^isArray}} - {{^isString}} - {{^isNumber}} - {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}.toString()) } - {{/isNumber}} - {{#isNumber}} - {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } - {{/isNumber}} - {{/isString}} - {{#isString}} - {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } - {{/isString}} - {{/isArray}} - {{#isArray}} - for (x in {{paramName}} ?: listOf()) { - append("{{{baseName}}}", x.toString()) - } - {{/isArray}} - {{/isFile}} - {{/formParams}} - } - {{/isMultipart}} - {{^isMultipart}} - ParametersBuilder().also { - {{#formParams}} - {{#isFile}} - {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}) } - {{/isFile}} - {{^isFile}} - {{^isArray}} - {{^isString}} - {{^isNumber}} - {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}.toString()) } - {{/isNumber}} - {{#isNumber}} - {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}) } - {{/isNumber}} - {{/isString}} - {{#isString}} - {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}) } - {{/isString}} - {{/isArray}} - {{#isArray}} - for (x in {{paramName}} ?: listOf()) { - append("{{{baseName}}}", x.toString()) - } - {{/isArray}} - {{/isFile}} - {{/formParams}} - }.build() - {{/isMultipart}} - {{/hasFormParams}} - {{^hasFormParams}} - io.ktor.client.utils.EmptyContent - {{/hasFormParams}} - {{/hasBodyParam}} - - val localVariableQuery = mutableMapOf>() - {{#queryParams}} - {{{paramName}}}?.apply { localVariableQuery["{{baseName}}"] = {{#isContainer}}toMultiValue(this, "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf("${{{paramName}}}"){{/isContainer}} } - {{/queryParams}} - - val localVariableHeaders = mutableMapOf() - {{#headerParams}} - {{{paramName}}}?.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } - {{/headerParams}} - - val localVariableConfig = RequestConfig( - RequestMethod.{{httpMethod}}, - "{{path}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", "${{{paramName}}}"){{/pathParams}}, - query = localVariableQuery, - headers = localVariableHeaders, - requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, - ) - - return {{#hasBodyParam}}jsonRequest{{/hasBodyParam}}{{^hasBodyParam}}{{#hasFormParams}}{{#isMultipart}}multipartFormRequest{{/isMultipart}}{{^isMultipart}}urlEncodedFormRequest{{/isMultipart}}{{/hasFormParams}}{{^hasFormParams}}request{{/hasFormParams}}{{/hasBodyParam}}( - localVariableConfig, - localVariableBody, - localVariableAuthNames - ).wrap() - } - - {{/operation}} - } -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache deleted file mode 100644 index 618fd7a88903..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/ApiKeyAuth.kt.mustache +++ /dev/null @@ -1,16 +0,0 @@ -package {{packageName}}.auth - -class ApiKeyAuth(private val location: String, val paramName: String) : Authentication { - var apiKey: String? = null - var apiKeyPrefix: String? = null - - override fun apply(query: MutableMap>, headers: MutableMap) { - val key: String = apiKey ?: return - val prefix: String? = apiKeyPrefix - val value: String = if (prefix != null) "$prefix $key" else key - when (location) { - "query" -> query[paramName] = listOf(value) - "header" -> headers[paramName] = value - } - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache deleted file mode 100644 index 1aab9156d98b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/Authentication.kt.mustache +++ /dev/null @@ -1,13 +0,0 @@ -package {{packageName}}.auth - -interface Authentication { - - /** - * Apply authentication settings to header and query params. - * - * @param query Query parameters. - * @param headers Header parameters. - */ - fun apply(query: MutableMap>, headers: MutableMap) - -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache deleted file mode 100644 index 26325424e5dd..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBasicAuth.kt.mustache +++ /dev/null @@ -1,17 +0,0 @@ -package {{packageName}}.auth - -import io.ktor.util.InternalAPI -import io.ktor.util.encodeBase64 - -class HttpBasicAuth : Authentication { - var username: String? = null - var password: String? = null - - @OptIn(InternalAPI::class) - override fun apply(query: MutableMap>, headers: MutableMap) { - if (username == null && password == null) return - val str = (username ?: "") + ":" + (password ?: "") - val auth = str.encodeBase64() - headers["Authorization"] = "Basic $auth" - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache deleted file mode 100644 index 982389d09609..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/HttpBearerAuth.kt.mustache +++ /dev/null @@ -1,14 +0,0 @@ -package {{packageName}}.auth - -class HttpBearerAuth(private val scheme: String?) : Authentication { - var bearerToken: String? = null - - override fun apply(query: MutableMap>, headers: MutableMap) { - val token: String = bearerToken ?: return - headers["Authorization"] = (if (scheme != null) upperCaseBearer(scheme)!! + " " else "") + token - } - - private fun upperCaseBearer(scheme: String): String? { - return if ("bearer".equals(scheme, ignoreCase = true)) "Bearer" else scheme - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache deleted file mode 100644 index 98bb449a6096..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/auth/OAuth.kt.mustache +++ /dev/null @@ -1,10 +0,0 @@ -package {{packageName}}.auth - -class OAuth : Authentication { - var accessToken: String? = null - - override fun apply(query: MutableMap>, headers: MutableMap) { - val token: String = accessToken ?: return - headers["Authorization"] = "Bearer $token" - } -} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index c189f9f9ed93..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,229 +0,0 @@ -package {{packageName}}.infrastructure - - -import io.ktor.client.HttpClient -import io.ktor.client.HttpClientConfig -import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.request.forms.FormDataContent -import io.ktor.client.request.forms.MultiPartFormDataContent -import io.ktor.client.request.header -import io.ktor.client.request.parameter -import io.ktor.client.request.request -import io.ktor.client.request.setBody -import io.ktor.client.statement.HttpResponse -import io.ktor.http.contentType -import io.ktor.http.ContentType -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpMethod -import io.ktor.http.Parameters -import io.ktor.http.URLBuilder -import io.ktor.http.content.PartData -import io.ktor.http.encodeURLQueryComponent -import io.ktor.http.encodedPath -import io.ktor.http.takeFrom -{{#gson}} -import io.ktor.serialization.gson.* -import com.google.gson.GsonBuilder -import java.text.DateFormat -{{/gson}} -{{#jackson}} -import io.ktor.serialization.jackson.* -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.SerializationFeature -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.core.util.DefaultIndenter -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter -{{/jackson}} -import {{packageName}}.auth.* - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient( - private val baseUrl: String, - httpClientEngine: HttpClientEngine?, - httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, - {{#gson}} - jsonBlock: GsonBuilder.() -> Unit = JSON_DEFAULT, - {{/gson}} - {{#jackson}} - jsonBlock: ObjectMapper.() -> Unit = JSON_DEFAULT, - {{/jackson}} -) { - - private val clientConfig: (HttpClientConfig<*>) -> Unit by lazy { - { - it.install(ContentNegotiation) { - {{#gson}} - gson { jsonBlock() } - {{/gson}} - {{#jackson}} - jackson { jsonBlock() } - {{/jackson}} - } - httpClientConfig?.invoke(it) - } - } - - private val client: HttpClient by lazy { - httpClientEngine?.let { HttpClient(it, clientConfig) } ?: HttpClient(clientConfig) - } - - {{#hasAuthMethods}} - private val authentications: kotlin.collections.Map by lazy { - mapOf({{#authMethods}}{{#isBasic}}{{#isBasicBasic}} - "{{name}}" to HttpBasicAuth(){{/isBasicBasic}}{{#isBasicBearer}} - "{{name}}" to HttpBearerAuth("{{scheme}}"){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} - "{{name}}" to ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"){{/isApiKey}}{{#isOAuth}} - "{{name}}" to OAuth(){{/isOAuth}}{{^-last}}, {{/-last}}{{/authMethods}}) - } - {{/hasAuthMethods}} - {{^hasAuthMethods}} - private val authentications: kotlin.collections.Map? = null - {{/hasAuthMethods}} - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - const val BASE_URL = "{{{basePath}}}" - {{#gson}} - val JSON_DEFAULT : GsonBuilder.() -> Unit = { - setDateFormat(DateFormat.LONG) - setPrettyPrinting() - } - {{/gson}} - {{#jackson}} - val JSON_DEFAULT: ObjectMapper.() -> Unit = { - configure(SerializationFeature.INDENT_OUTPUT, true) - setDefaultPrettyPrinter(DefaultPrettyPrinter().apply { - indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance) - indentObjectsWith(DefaultIndenter(" ", "\n")) - }) - registerModule(JavaTimeModule()) - } - {{/jackson}} - protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType) - } - - /** - * Set the username for the first HTTP basic authentication. - * - * @param username Username - */ - fun setUsername(username: String) { - val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? - ?: throw Exception("No HTTP basic authentication configured") - auth.username = username - } - - /** - * Set the password for the first HTTP basic authentication. - * - * @param password Password - */ - fun setPassword(password: String) { - val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? - ?: throw Exception("No HTTP basic authentication configured") - auth.password = password - } - - /** - * Set the API key value for the first API key authentication. - * - * @param apiKey API key - * @param paramName The name of the API key parameter, or null or set the first key. - */ - fun setApiKey(apiKey: String, paramName: String? = null) { - val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth? - ?: throw Exception("No API key authentication configured") - auth.apiKey = apiKey - } - - /** - * Set the API key prefix for the first API key authentication. - * - * @param apiKeyPrefix API key prefix - * @param paramName The name of the API key parameter, or null or set the first key. - */ - fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) { - val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth? - ?: throw Exception("No API key authentication configured") - auth.apiKeyPrefix = apiKeyPrefix - } - - /** - * Set the access token for the first OAuth2 authentication. - * - * @param accessToken Access token - */ - fun setAccessToken(accessToken: String) { - val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth? - ?: throw Exception("No OAuth2 authentication configured") - auth.accessToken = accessToken - } - - /** - * Set the access token for the first Bearer authentication. - * - * @param bearerToken The bearer token. - */ - fun setBearerToken(bearerToken: String) { - val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth? - ?: throw Exception("No Bearer authentication configured") - auth.bearerToken = bearerToken - } - - protected suspend fun multipartFormRequest(requestConfig: RequestConfig, body: kotlin.collections.List?, authNames: kotlin.collections.List): HttpResponse { - return request(requestConfig, MultiPartFormDataContent(body ?: listOf()), authNames) - } - - protected suspend fun urlEncodedFormRequest(requestConfig: RequestConfig, body: Parameters?, authNames: kotlin.collections.List): HttpResponse { - return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames) - } - - protected suspend fun jsonRequest(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse = request(requestConfig, body, authNames) - - protected suspend fun request(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse { - requestConfig.updateForAuth(authNames) - val headers = requestConfig.headers - - return client.request { - this.url { - this.takeFrom(URLBuilder(baseUrl)) - appendPath(requestConfig.path.trimStart('/').split('/')) - requestConfig.query.forEach { query -> - query.value.forEach { value -> - parameter(query.key, value) - } - } - } - this.method = requestConfig.method.httpMethod - headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) } - if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) { - val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) } - ?: ContentType.Application.Json) - this.contentType(contentType) - setBody(body) - } - } - } - - private fun RequestConfig.updateForAuth(authNames: kotlin.collections.List) { - for (authName in authNames) { - val auth = authentications?.get(authName) ?: throw Exception("Authentication undefined: $authName") - auth.apply(query, headers) - } - } - - private fun URLBuilder.appendPath(components: kotlin.collections.List): URLBuilder = apply { - encodedPath = encodedPath.trimEnd('/') + components.joinToString("/", prefix = "/") { it.encodeURLQueryComponent() } - } - - private val RequestMethod.httpMethod: HttpMethod - get() = when (this) { - RequestMethod.DELETE -> HttpMethod.Delete - RequestMethod.GET -> HttpMethod.Get - RequestMethod.HEAD -> HttpMethod.Head - RequestMethod.PATCH -> HttpMethod.Patch - RequestMethod.PUT -> HttpMethod.Put - RequestMethod.POST -> HttpMethod.Post - RequestMethod.OPTIONS -> HttpMethod.Options - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache deleted file mode 100644 index 87a68f3084e5..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-ktor/infrastructure/HttpResponse.kt.mustache +++ /dev/null @@ -1,51 +0,0 @@ -package {{packageName}}.infrastructure - -import io.ktor.http.Headers -import io.ktor.http.isSuccess -import io.ktor.util.reflect.TypeInfo -import io.ktor.util.reflect.typeInfo - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class HttpResponse(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider) { - val status: Int = response.status.value - val success: Boolean = response.status.isSuccess() - val headers: Map> = response.headers.mapEntries() - suspend fun body(): T = provider.body(response) - suspend fun typedBody(type: TypeInfo): V = provider.typedBody(response, type) - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private fun Headers.mapEntries(): Map> { - val result = mutableMapOf>() - entries().forEach { result[it.key] = it.value } - return result - } - } -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}interface BodyProvider { - suspend fun body(response: io.ktor.client.statement.HttpResponse): T - suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider(private val type: TypeInfo) : BodyProvider { - @Suppress("UNCHECKED_CAST") - override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = - response.call.body(type) as T - - @Suppress("UNCHECKED_CAST") - override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = - response.call.body(type) as V -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider(private val provider: BodyProvider, private val block: S.() -> T) : BodyProvider { - override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = - block(provider.body(response)) - - override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = - provider.typedBody(response, type) -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}inline fun io.ktor.client.statement.HttpResponse.wrap(): HttpResponse = - HttpResponse(this, TypedBodyProvider(typeInfo())) - -{{#nonPublicApi}}internal {{/nonPublicApi}}fun HttpResponse.map(block: T.() -> V): HttpResponse = - HttpResponse(response, MappedBodyProvider(provider, block)) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache deleted file mode 100644 index 642794399125..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/api.mustache +++ /dev/null @@ -1,248 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -import java.io.IOException -import okhttp3.Call -import okhttp3.HttpUrl - -{{#imports}}import {{import}} -{{/imports}} - -{{^multiplatform}} -{{#gson}} -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -{{/moshi}} -{{#jackson}} -import com.fasterxml.jackson.annotation.JsonProperty -{{/jackson}} -{{#kotlinx_serialization}} -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -{{/kotlinx_serialization}} -{{/multiplatform}} -{{#multiplatform}} -import kotlinx.serialization.* -{{/multiplatform}} - -{{^doNotUseRxAndCoroutines}} -{{#useCoroutines}} -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -{{/useCoroutines}} -{{/doNotUseRxAndCoroutines}} -import {{packageName}}.infrastructure.ApiClient -import {{packageName}}.infrastructure.ApiResponse -import {{packageName}}.infrastructure.ClientException -import {{packageName}}.infrastructure.ClientError -import {{packageName}}.infrastructure.ServerException -import {{packageName}}.infrastructure.ServerError -import {{packageName}}.infrastructure.MultiValueMap -import {{packageName}}.infrastructure.PartConfig -import {{packageName}}.infrastructure.RequestConfig -import {{packageName}}.infrastructure.RequestMethod -import {{packageName}}.infrastructure.ResponseType -import {{packageName}}.infrastructure.Success -import {{packageName}}.infrastructure.toMultiValue - -{{#operations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(basePath: kotlin.String = defaultBasePath, client: Call.Factory = ApiClient.defaultClient) : ApiClient(basePath, client) { - companion object { - @JvmStatic - val defaultBasePath: String by lazy { - System.getProperties().getProperty(ApiClient.baseUrlKey, "{{{basePath}}}") - } - } - - {{#operation}} - {{#allParams}} - {{#isEnum}} - /** - * enum for parameter {{paramName}} - */ - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{^enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^multiplatform}} - {{#moshi}} - @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/moshi}} - {{#gson}} - @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/multiplatform}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - {{#enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^multiplatform}} - {{#moshi}} - @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/moshi}} - {{#gson}} - @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} - {{/multiplatform}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - - /** - * Override [toString()] to avoid using the enum variable name as the value, and instead use - * the actual value defined in the API spec file. - * - * This solves a problem when the variable name and its value are different, and ensures that - * the client sends the correct enum values to the server always. - */ - override fun toString(): kotlin.String = "$value" - } - - {{/isEnum}} - {{/allParams}} - /** - * {{summary}} - * {{notes}} - {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{#nullableReturnType}} or null{{/nullableReturnType}}{{/returnType}}{{^returnType}}void{{/returnType}} - * @throws IllegalStateException If the request is not correctly configured - * @throws IOException Rethrows the OkHttp execute method exception - * @throws UnsupportedOperationException If the API returns an informational or redirection response - * @throws ClientException If the API returns a client error response - * @throws ServerException If the API returns a server error response - */{{#returnType}} - @Suppress("UNCHECKED_CAST"){{/returnType}} - @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - {{^doNotUseRxAndCoroutines}}{{#useCoroutines}}suspend {{/useCoroutines}}{{/doNotUseRxAndCoroutines}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : {{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}{{^doNotUseRxAndCoroutines}}{{#useCoroutines}} = withContext(Dispatchers.IO){{/useCoroutines}}{{/doNotUseRxAndCoroutines}} { - {{#isDeprecated}} - @Suppress("DEPRECATION") - {{/isDeprecated}} - val localVarResponse = {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - - return{{^doNotUseRxAndCoroutines}}{{#useCoroutines}}@withContext{{/useCoroutines}}{{/doNotUseRxAndCoroutines}} when (localVarResponse.responseType) { - ResponseType.Success -> {{#returnType}}(localVarResponse as Success<*>).data as {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} - ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.") - ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.") - ResponseType.ClientError -> { - val localVarError = localVarResponse as ClientError<*> - throw ClientException("Client error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) - } - ResponseType.ServerError -> { - val localVarError = localVarResponse as ServerError<*> - throw ServerException("Server error : ${localVarError.statusCode} ${localVarError.message.orEmpty()} ${localVarError.body}", localVarError.statusCode, localVarResponse) - } - } - } - - /** - * {{summary}} - * {{notes}} - {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}}* @return ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}> - * @throws IllegalStateException If the request is not correctly configured - * @throws IOException Rethrows the OkHttp execute method exception - */{{#returnType}} - @Suppress("UNCHECKED_CAST"){{/returnType}} - @Throws(IllegalStateException::class, IOException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - {{^doNotUseRxAndCoroutines}}{{#useCoroutines}}suspend {{/useCoroutines}}{{/doNotUseRxAndCoroutines}}fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}>{{^doNotUseRxAndCoroutines}}{{#useCoroutines}} = withContext(Dispatchers.IO){{/useCoroutines}}{{/doNotUseRxAndCoroutines}} { - {{#isDeprecated}} - @Suppress("DEPRECATION") - {{/isDeprecated}} - val localVariableConfig = {{operationId}}RequestConfig({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - - return{{^doNotUseRxAndCoroutines}}{{#useCoroutines}}@withContext{{/useCoroutines}}{{/doNotUseRxAndCoroutines}} request<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}, {{{returnType}}}{{^returnType}}Unit{{/returnType}}>( - localVariableConfig - ) - } - - /** - * To obtain the request config of the operation {{operationId}} - * - {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}}* @return RequestConfig - */ - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}RequestConfig({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : RequestConfig<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}> { - val localVariableBody = {{#hasBodyParam}}{{! - }}{{#bodyParams}}{{{paramName}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{! - }}{{^hasFormParams}}null{{/hasFormParams}}{{! - }}{{#hasFormParams}}mapOf({{#formParams}} - "{{#lambda.escapeDollar}}{{{baseName}}}{{/lambda.escapeDollar}}" to PartConfig(body = {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}, headers = mutableMapOf({{#contentType}}"Content-Type" to "{{contentType}}"{{/contentType}})),{{! - }}{{/formParams}}){{/hasFormParams}}{{! - }}{{/hasBodyParam}} - val localVariableQuery: MultiValueMap = {{^hasQueryParams}}mutableMapOf() -{{/hasQueryParams}}{{#hasQueryParams}}mutableMapOf>() - .apply { - {{#queryParams}} - {{^required}} - if ({{{paramName}}} != null) { - put("{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.value{{/isString}}{{^isString}}{{{paramName}}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}}) - } - {{/required}} - {{#required}} - {{#isNullable}} - if ({{{paramName}}} != null) { - put("{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.value{{/isString}}{{^isString}}{{{paramName}}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}}) - } - {{/isNullable}} - {{^isNullable}} - put("{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{#isEnum}}{{#isString}}{{{paramName}}}.value{{/isString}}{{^isString}}{{{paramName}}}.toString(){{/isString}}{{/isEnum}}{{^isEnum}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}{{/isEnum}}){{/isContainer}}) - {{/isNullable}} - {{/required}} - {{/queryParams}} - } - {{/hasQueryParams}} - val localVariableHeaders: MutableMap = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}}) - {{#headerParams}} - {{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } - {{/headerParams}} - {{^hasFormParams}}{{#hasConsumes}}{{#consumes}}localVariableHeaders["Content-Type"] = "{{{mediaType}}}" - {{/consumes}}{{/hasConsumes}}{{/hasFormParams}}{{#hasProduces}}localVariableHeaders["Accept"] = "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}" -{{/hasProduces}} - - return RequestConfig( - method = RequestMethod.{{httpMethod}}, - path = "{{path}}"{{#pathParams}}.replace("{"+"{{#lambda.escapeDollar}}{{baseName}}{{/lambda.escapeDollar}}"+"}", encodeURIComponent({{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}.toString(){{/isContainer}})){{/pathParams}}, - query = localVariableQuery, - headers = localVariableHeaders, - requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, - body = localVariableBody - ) - } - - {{/operation}} - - private fun encodeURIComponent(uriComponent: kotlin.String): kotlin.String = - HttpUrl.Builder().scheme("http").host("localhost").addPathSegment(uriComponent).build().encodedPathSegments[0] -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index 8aabf649f883..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,448 +0,0 @@ -package {{packageName}}.infrastructure - -{{#supportAndroidApiLevel25AndBelow}} -import android.os.Build -{{/supportAndroidApiLevel25AndBelow}} -import okhttp3.OkHttpClient -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.asRequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.FormBody -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.ResponseBody -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.Request -import okhttp3.Headers -import okhttp3.Headers.Companion.toHeaders -import okhttp3.MultipartBody -import okhttp3.Call -import okhttp3.Callback -import okhttp3.Response -import java.io.BufferedWriter -import java.io.File -import java.io.FileWriter -import java.io.IOException -import java.net.URLConnection -{{^threetenbp}} -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.OffsetDateTime -import java.time.OffsetTime -{{/threetenbp}} -import java.util.Locale -import java.util.regex.Pattern -{{#useCoroutines}} -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlinx.coroutines.suspendCancellableCoroutine -{{/useCoroutines}} -{{#kotlinx_serialization}} -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -{{/kotlinx_serialization}} -{{#threetenbp}} -import org.threeten.bp.LocalDate -import org.threeten.bp.LocalDateTime -import org.threeten.bp.LocalTime -import org.threeten.bp.OffsetDateTime -import org.threeten.bp.OffsetTime -{{/threetenbp}} -{{#gson}} -import com.google.gson.reflect.TypeToken -{{/gson}} -{{#jackson}} -import com.fasterxml.jackson.core.type.TypeReference -{{/jackson}} -{{#moshi}} -import com.squareup.moshi.adapter -{{/moshi}} - -{{#nonPublicApi}}internal {{/nonPublicApi}}val EMPTY_REQUEST: RequestBody = ByteArray(0).toRequestBody() - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(val baseUrl: String, val client: Call.Factory = defaultClient) { - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - protected const val ContentType = "Content-Type" - protected const val Accept = "Accept" - protected const val Authorization = "Authorization" - protected const val JsonMediaType = "application/json" - protected const val FormDataMediaType = "multipart/form-data" - protected const val FormUrlEncMediaType = "application/x-www-form-urlencoded" - protected const val XmlMediaType = "application/xml" - protected const val OctetMediaType = "application/octet-stream" - - val apiKey: MutableMap = mutableMapOf() - val apiKeyPrefix: MutableMap = mutableMapOf() - var username: String? = null - var password: String? = null - var accessToken: String? = null - const val baseUrlKey = "{{packageName}}.baseUrl" - - @JvmStatic - val defaultClient: OkHttpClient by lazy { - builder.build() - } - - @JvmStatic - val builder: OkHttpClient.Builder = OkHttpClient.Builder() - } - - /** - * Guess Content-Type header from the given file (defaults to "application/octet-stream"). - * - * @param file The given file - * @return The guessed Content-Type - */ - protected fun guessContentTypeFromFile(file: File): String { - val contentType = URLConnection.guessContentTypeFromName(file.name) - return contentType ?: "application/octet-stream" - } - - protected inline fun requestBody(content: T, mediaType: String?): RequestBody = - when { - content is File -> content.asRequestBody((mediaType ?: guessContentTypeFromFile(content)).toMediaTypeOrNull()) - mediaType == FormDataMediaType -> - MultipartBody.Builder() - .setType(MultipartBody.FORM) - .apply { - // content's type *must* be Map> - @Suppress("UNCHECKED_CAST") - (content as Map>).forEach { (name, part) -> - if (part.body is File) { - val partHeaders = part.headers.toMutableMap() + - ("Content-Disposition" to "form-data; name=\"$name\"; filename=\"${part.body.name}\"") - val fileMediaType = guessContentTypeFromFile(part.body).toMediaTypeOrNull() - addPart( - partHeaders.toHeaders(), - part.body.asRequestBody(fileMediaType) - ) - } else { - val partHeaders = part.headers.toMutableMap() + - ("Content-Disposition" to "form-data; name=\"$name\"") - addPart( - partHeaders.toHeaders(), - parameterToString(part.body).toRequestBody(null) - ) - } - } - }.build() - mediaType == FormUrlEncMediaType -> { - FormBody.Builder().apply { - // content's type *must* be Map> - @Suppress("UNCHECKED_CAST") - (content as Map>).forEach { (name, part) -> - add(name, parameterToString(part.body)) - } - }.build() - } - mediaType == null || mediaType.startsWith("application/") && mediaType.endsWith("json") -> - if (content == null) { - EMPTY_REQUEST - } else { - {{#moshi}} - Serializer.moshi.adapter(T::class.java).toJson(content) - {{/moshi}} - {{#gson}} - Serializer.gson.toJson(content, T::class.java) - {{/gson}} - {{#jackson}} - Serializer.jacksonObjectMapper.writeValueAsString(content) - {{/jackson}} - {{#kotlinx_serialization}} - Serializer.kotlinxSerializationJson.encodeToString(content) - {{/kotlinx_serialization}} - .toRequestBody((mediaType ?: JsonMediaType).toMediaTypeOrNull()) - } - mediaType == XmlMediaType -> throw UnsupportedOperationException("xml not currently supported.") - mediaType == OctetMediaType && content is ByteArray -> - content.toRequestBody(OctetMediaType.toMediaTypeOrNull()) - // TODO: this should be extended with other serializers - else -> throw UnsupportedOperationException("requestBody currently only supports JSON body, byte body and File body.") - } - - {{#moshi}} - @OptIn(ExperimentalStdlibApi::class) - {{/moshi}} - protected inline fun responseBody(response: Response, mediaType: String? = JsonMediaType): T? { - val body = response.body - if(body == null) { - return null - } - if (T::class.java == File::class.java) { - // return tempFile - val contentDisposition = response.header("Content-Disposition") - - val fileName = if (contentDisposition != null) { - // Get filename from the Content-Disposition header. - val pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?") - val matcher = pattern.matcher(contentDisposition) - if (matcher.find()) { - matcher.group(1) - ?.replace(".*[/\\\\]", "") - ?.replace(";", "") - } else { - null - } - } else { - null - } - - var prefix: String? - val suffix: String? - if (fileName == null) { - prefix = "download" - suffix = "" - } else { - val pos = fileName.lastIndexOf(".") - if (pos == -1) { - prefix = fileName - suffix = null - } else { - prefix = fileName.substring(0, pos) - suffix = fileName.substring(pos) - } - // Files.createTempFile requires the prefix to be at least three characters long - if (prefix.length < 3) { - prefix = "download" - } - } - - {{^supportAndroidApiLevel25AndBelow}} - // Attention: if you are developing an android app that supports API Level 25 and bellow, please check flag supportAndroidApiLevel25AndBelow in https://openapi-generator.tech/docs/generators/kotlin#config-options - val tempFile = java.nio.file.Files.createTempFile(prefix, suffix).toFile() - {{/supportAndroidApiLevel25AndBelow}} - {{#supportAndroidApiLevel25AndBelow}} - val tempFile = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - java.nio.file.Files.createTempFile(prefix, suffix).toFile() - } else { - @Suppress("DEPRECATION") - createTempFile(prefix, suffix) - } - {{/supportAndroidApiLevel25AndBelow}} - tempFile.deleteOnExit() - body.byteStream().use { inputStream -> - tempFile.outputStream().use { tempFileOutputStream -> - inputStream.copyTo(tempFileOutputStream) - } - } - return tempFile as T - } - - return when { - mediaType == null || (mediaType.startsWith("application/") && mediaType.endsWith("json")) -> { - val bodyContent = body.string() - if (bodyContent.isEmpty()) { - return null - } - {{#moshi}}Serializer.moshi.adapter().fromJson(bodyContent){{/moshi}}{{! - }}{{#gson}}Serializer.gson.fromJson(bodyContent, (object: TypeToken(){}).getType()){{/gson}}{{! - }}{{#jackson}}Serializer.jacksonObjectMapper.readValue(bodyContent, object: TypeReference() {}){{/jackson}}{{! - }}{{#kotlinx_serialization}}Serializer.kotlinxSerializationJson.decodeFromString(bodyContent){{/kotlinx_serialization}} - } - mediaType == OctetMediaType -> body.bytes() as? T - else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.") - } - } - - {{#hasAuthMethods}} - protected fun updateAuthParams(requestConfig: RequestConfig) { - {{#authMethods}} - {{#isApiKey}} - {{#isKeyInHeader}} - if (requestConfig.headers["{{keyParamName}}"].isNullOrEmpty()) { - {{/isKeyInHeader}} - {{#isKeyInQuery}} - if (requestConfig.query["{{keyParamName}}"].isNullOrEmpty()) { - {{/isKeyInQuery}} - if (apiKey["{{keyParamName}}"] != null) { - if (apiKeyPrefix["{{keyParamName}}"] != null) { - {{#isKeyInHeader}} - requestConfig.headers["{{keyParamName}}"] = apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!! - {{/isKeyInHeader}} - {{#isKeyInQuery}} - requestConfig.query["{{keyParamName}}"] = listOf(apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!) - {{/isKeyInQuery}} - } else { - {{#isKeyInHeader}} - requestConfig.headers["{{keyParamName}}"] = apiKey["{{keyParamName}}"]!! - {{/isKeyInHeader}} - {{#isKeyInQuery}} - requestConfig.query["{{keyParamName}}"] = listOf(apiKey["{{keyParamName}}"]!!) - {{/isKeyInQuery}} - } - } - {{#isKeyInQuery}} - } - {{/isKeyInQuery}} - {{#isKeyInHeader}} - } - {{/isKeyInHeader}} - {{/isApiKey}} - {{#isBasic}} - {{#isBasicBasic}} - if (requestConfig.headers[Authorization].isNullOrEmpty()) { - username?.let { username -> - password?.let { password -> - requestConfig.headers[Authorization] = okhttp3.Credentials.basic(username, password) - } - } - } - {{/isBasicBasic}} - {{#isBasicBearer}} - if (requestConfig.headers[Authorization].isNullOrEmpty()) { - accessToken?.let { accessToken -> - requestConfig.headers[Authorization] = "Bearer $accessToken" - } - } - {{/isBasicBearer}} - {{/isBasic}} - {{#isOAuth}} - if (requestConfig.headers[Authorization].isNullOrEmpty()) { - accessToken?.let { accessToken -> - requestConfig.headers[Authorization] = "Bearer $accessToken " - } - } - {{/isOAuth}} - {{/authMethods}} - } - {{/hasAuthMethods}} - - protected {{#useCoroutines}}suspend {{/useCoroutines}}inline fun request(requestConfig: RequestConfig): ApiResponse { - val httpUrl = baseUrl.toHttpUrlOrNull() ?: throw IllegalStateException("baseUrl is invalid.") - {{#hasAuthMethods}} - - // take authMethod from operation - updateAuthParams(requestConfig) - {{/hasAuthMethods}} - - val url = httpUrl.newBuilder() - .addEncodedPathSegments(requestConfig.path.trimStart('/')) - .apply { - requestConfig.query.forEach { query -> - query.value.forEach { queryValue -> - addQueryParameter(query.key, queryValue) - } - } - }.build() - - // take content-type/accept from spec or set to default (application/json) if not defined - if (requestConfig.body != null && requestConfig.headers[ContentType].isNullOrEmpty()) { - requestConfig.headers[ContentType] = JsonMediaType - } - if (requestConfig.headers[Accept].isNullOrEmpty()) { - requestConfig.headers[Accept] = JsonMediaType - } - val headers = requestConfig.headers - - if (headers[Accept].isNullOrEmpty()) { - throw kotlin.IllegalStateException("Missing Accept header. This is required.") - } - - val contentType = if (headers[ContentType] != null) { - // TODO: support multiple contentType options here. - (headers[ContentType] as String).substringBefore(";").lowercase(Locale.US) - } else { - null - } - - val request = when (requestConfig.method) { - RequestMethod.DELETE -> Request.Builder().url(url).delete(requestBody(requestConfig.body, contentType)) - RequestMethod.GET -> Request.Builder().url(url) - RequestMethod.HEAD -> Request.Builder().url(url).head() - RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(requestConfig.body, contentType)) - RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(requestConfig.body, contentType)) - RequestMethod.POST -> Request.Builder().url(url).post(requestBody(requestConfig.body, contentType)) - RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null) - }.apply { - headers.forEach { header -> addHeader(header.key, header.value) } - }.build() - - {{#useCoroutines}} - val response: Response = suspendCancellableCoroutine { continuation -> - val call = client.newCall(request) - continuation.invokeOnCancellation { call.cancel() } - call.enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - continuation.resumeWithException(e) - } - override fun onResponse(call: Call, response: Response) { - continuation.resume(response) - } - }) - } - {{/useCoroutines}} - {{^useCoroutines}} - val response = client.newCall(request).execute() - {{/useCoroutines}} - - val accept = response.header(ContentType)?.substringBefore(";")?.lowercase(Locale.US) - - // TODO: handle specific mapping types. e.g. Map> - @Suppress("UNNECESSARY_SAFE_CALL") - return response.use { - when { - it.isRedirect -> Redirection( - it.code, - it.headers.toMultimap() - ) - it.isInformational -> Informational( - it.message, - it.code, - it.headers.toMultimap() - ) - it.isSuccessful -> Success( - responseBody(it, accept), - it.code, - it.headers.toMultimap() - ) - it.isClientError -> ClientError( - it.message, - it.body?.string(), - it.code, - it.headers.toMultimap() - ) - else -> ServerError( - it.message, - it.body?.string(), - it.code, - it.headers.toMultimap() - ) - } - } - } - - protected fun parameterToString(value: Any?): String = when (value) { - null -> "" - is Array<*> -> toMultiValue(value, "csv").toString() - is Iterable<*> -> toMultiValue(value, "csv").toString() - is OffsetDateTime, is OffsetTime, is LocalDateTime, is LocalDate, is LocalTime -> - parseDateToQueryString(value) - else -> value.toString() - } - - protected inline fun parseDateToQueryString(value : T): String { - {{#toJson}} - /* - .replace("\"", "") converts the json object string to an actual string for the query parameter. - The moshi or gson adapter allows a more generic solution instead of trying to use a native - formatter. It also easily allows to provide a simple way to define a custom date format pattern - inside a gson/moshi adapter. - */ - {{#moshi}} - return Serializer.moshi.adapter(T::class.java).toJson(value).replace("\"", "") - {{/moshi}} - {{#gson}} - return Serializer.gson.toJson(value, T::class.java).replace("\"", "") - {{/gson}} - {{#jackson}} - return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") - {{/jackson}} - {{#kotlinx_serialization}} - return Serializer.kotlinxSerializationJson.encodeToString(value).replace("\"", "") - {{/kotlinx_serialization}} - {{/toJson}} - {{^toJson}} - return value.toString() - {{/toJson}} - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache deleted file mode 100644 index d529ad5599f1..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ApiResponse.kt.mustache +++ /dev/null @@ -1,43 +0,0 @@ -package {{packageName}}.infrastructure - -{{#nonPublicApi}}internal {{/nonPublicApi}}enum class ResponseType { - Success, Informational, Redirection, ClientError, ServerError -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}interface Response - -{{#nonPublicApi}}internal {{/nonPublicApi}}abstract class ApiResponse(val responseType: ResponseType): Response { - abstract val statusCode: Int - abstract val headers: Map> -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class Success( - val data: T{{#nullableReturnType}}?{{/nullableReturnType}}, - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -): ApiResponse(ResponseType.Success) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class Informational( - val statusText: String, - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -) : ApiResponse(ResponseType.Informational) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class Redirection( - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -) : ApiResponse(ResponseType.Redirection) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class ClientError( - val message: String? = null, - val body: Any? = null, - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -) : ApiResponse(ResponseType.ClientError) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class ServerError( - val message: String? = null, - val body: Any? = null, - override val statusCode: Int = -1, - override val headers: Map> -): ApiResponse(ResponseType.ServerError) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache deleted file mode 100644 index 1357da8d599c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/Errors.kt.mustache +++ /dev/null @@ -1,18 +0,0 @@ -@file:Suppress("unused") -package {{packageName}}.infrastructure - -import java.lang.RuntimeException - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ClientException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private const val serialVersionUID: Long = 123L - } -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ServerException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private const val serialVersionUID: Long = 456L - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache deleted file mode 100644 index 8bf8d157f6c2..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-okhttp/infrastructure/ResponseExtensions.kt.mustache +++ /dev/null @@ -1,24 +0,0 @@ -package {{packageName}}.infrastructure - -import okhttp3.Response - -/** - * Provides an extension to evaluation whether the response is a 1xx code - */ -{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isInformational : Boolean get() = this.code in 100..199 - -/** - * Provides an extension to evaluation whether the response is a 3xx code - */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isRedirect : Boolean get() = this.code in 300..399 - -/** - * Provides an extension to evaluation whether the response is a 4xx code - */ -{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isClientError : Boolean get() = this.code in 400..499 - -/** - * Provides an extension to evaluation whether the response is a 5xx (Standard) through 999 (non-standard) code - */ -{{#nonPublicApi}}internal {{/nonPublicApi}}val Response.isServerError : Boolean get() = this.code in 500..999 diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache deleted file mode 100644 index 4381539414cd..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api.mustache +++ /dev/null @@ -1,161 +0,0 @@ -package {{apiPackage}} - -import {{packageName}}.infrastructure.CollectionFormats.* -import retrofit2.http.* -{{#doNotUseRxAndCoroutines}} -import retrofit2.Call -{{/doNotUseRxAndCoroutines}} -{{^doNotUseRxAndCoroutines}} -{{#useCoroutines}} -import retrofit2.Response -{{/useCoroutines}} -{{/doNotUseRxAndCoroutines}} -import okhttp3.RequestBody -{{#isResponseFile}} -import okhttp3.ResponseBody -{{/isResponseFile}} -{{#isMultipart}} -import okhttp3.MultipartBody -{{/isMultipart}} -{{^doNotUseRxAndCoroutines}} -{{#useRxJava}} -import rx.Observable -{{/useRxJava}} -{{#useRxJava2}} -import io.reactivex.Single -{{/useRxJava2}} -{{#useRxJava3}} -import io.reactivex.rxjava3.core.Single -{{/useRxJava3}} -{{^returnType}} -{{#useRxJava2}} -import io.reactivex.Completable -{{/useRxJava2}} -{{#useRxJava3}} -import io.reactivex.rxjava3.core.Completable -{{/useRxJava3}} -{{/returnType}} -{{/doNotUseRxAndCoroutines}} -{{^multiplatform}} -{{#gson}} -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -{{/moshi}} -{{#jackson}} -import com.fasterxml.jackson.annotation.JsonProperty -{{/jackson}} -{{#kotlinx_serialization}} -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -{{/kotlinx_serialization}} -{{/multiplatform}} -{{#multiplatform}} -import kotlinx.serialization.* -{{/multiplatform}} - -{{#imports}}import {{import}} -{{/imports}} - -{{#operations}} -{{#x-kotlin-import-models}} -import {{{modelPackage}}}.* - -{{/x-kotlin-import-models}} -{{#x-kotlin-multipart-import}} -{{^isMultipart}} -import okhttp3.MultipartBody - -{{/isMultipart}} -{{/x-kotlin-multipart-import}} -{{#nonPublicApi}}internal {{/nonPublicApi}}interface {{classname}} { - {{#operation}} - {{#allParams}} - {{#isEnum}} - - /** - * enum for parameter {{paramName}} - */ - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{^enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^multiplatform}} - {{#moshi}} - @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/moshi}} - {{#gson}} - @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/multiplatform}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - {{#enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^-last}} - {{^multiplatform}} - {{#moshi}} - @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/moshi}} - {{#gson}} - @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/kotlinx_serialization}} - {{/multiplatform}} - {{#multiplatform}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/multiplatform}} - {{/-last}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - } - - {{/isEnum}} - {{/allParams}} - /** - * {{summary}} - * {{notes}} - * Responses:{{#responses}} - * - {{code}}: {{{message}}}{{/responses}} - *{{>paramJavadoc}} - * @return {{^useCoroutines}}[Call]<{{/useCoroutines}}{{#isResponseFile}}[ResponseBody]{{/isResponseFile}}{{^isResponseFile}}{{#returnType}}[{{{.}}}]{{/returnType}}{{^returnType}}[Unit]{{/returnType}}{{/isResponseFile}}{{^useCoroutines}}>{{/useCoroutines}} - */ - {{#isDeprecated}} - @Deprecated("This api was deprecated") - {{/isDeprecated}} - {{#formParams}} - {{#-first}} - {{#isMultipart}}@Multipart{{/isMultipart}}{{^isMultipart}}@FormUrlEncoded{{/isMultipart}} - {{/-first}} - {{/formParams}} - {{^formParams}} - {{#prioritizedContentTypes}} - {{#-first}} - @Headers("Content-Type:{{{mediaType}}}") - {{/-first}} - {{/prioritizedContentTypes}} - {{/formParams}} - @{{httpMethod}}("{{{path}}}") - {{^doNotUseRxAndCoroutines}}{{#useCoroutines}}suspend {{/useCoroutines}}{{/doNotUseRxAndCoroutines}}fun {{operationId}}({{^allParams}}){{/allParams}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{#-last}}){{/-last}}{{/allParams}}: {{^doNotUseRxAndCoroutines}}{{#useRxJava}}Observable<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isResponseFile}}>{{/useRxJava}}{{#useRxJava2}}{{#returnType}}Single<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}>{{/returnType}}{{^returnType}}Completable{{/returnType}}{{/useRxJava2}}{{#useRxJava3}}{{#returnType}}Single<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}>{{/returnType}}{{^returnType}}Completable{{/returnType}}{{/useRxJava3}}{{#useCoroutines}}Response<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isResponseFile}}>{{/useCoroutines}}{{/doNotUseRxAndCoroutines}}{{#doNotUseRxAndCoroutines}}Call<{{#isResponseFile}}ResponseBody{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isResponseFile}}>{{/doNotUseRxAndCoroutines}} - - {{/operation}} -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache deleted file mode 100644 index f764206df126..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/api_doc.mustache +++ /dev/null @@ -1,88 +0,0 @@ -# {{classname}}{{#description}} -{{.}}{{/description}} - -All URIs are relative to *{{basePath}}* - -| Method | HTTP request | Description | -| ------------- | ------------- | ------------- | -{{#operations}}{{#operation}}| [**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} | -{{/operation}}{{/operations}} - -{{#operations}} -{{#operation}} - -{{summary}}{{#notes}} - -{{.}}{{/notes}} - -### Example -```kotlin -// Import classes: -//import {{{packageName}}}.* -//import {{{packageName}}}.infrastructure.* -//import {{{modelPackage}}}.* - -val apiClient = ApiClient() -{{#authMethods}} -{{#isBasic}} -{{#isBasicBasic}} -apiClient.setCredentials("USERNAME", "PASSWORD") -{{/isBasicBasic}} -{{#isBasicBearer}} -apiClient.setBearerToken("TOKEN") -{{/isBasicBearer}} -{{/isBasic}} -{{/authMethods}} -val webService = apiClient.createWebservice({{{classname}}}::class.java) -{{#allParams}} -val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} -{{/allParams}} - -{{#useCoroutines}} -launch(Dispatchers.IO) { -{{/useCoroutines}} -{{#useCoroutines}} {{/useCoroutines}}{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}webService.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) -{{#useCoroutines}} -} -{{/useCoroutines}} -``` - -### Parameters -{{^allParams}} -This endpoint does not need any parameter. -{{/allParams}} -{{#allParams}} -{{#-last}} -| Name | Type | Description | Notes | -| ------------- | ------------- | ------------- | ------------- | -{{/-last}} -| **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} | -{{/allParams}} - -### Return type - -{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} - -### Authorization - -{{^authMethods}}No authorization required{{/authMethods}} -{{#authMethods}} -{{#isBasic}} -{{#isBasicBasic}} -Configure {{name}}: - ApiClient().setCredentials("USERNAME", "PASSWORD") -{{/isBasicBasic}} -{{#isBasicBearer}} -Configure {{name}}: - ApiClient().setBearerToken("TOKEN") -{{/isBasicBearer}} -{{/isBasic}} -{{/authMethods}} - -### HTTP request headers - - - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} - - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} - -{{/operation}} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache deleted file mode 100644 index 967980ae7fb5..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/ApiKeyAuth.kt.mustache +++ /dev/null @@ -1,50 +0,0 @@ -package {{packageName}}.auth - -import java.io.IOException -import java.net.URI -import java.net.URISyntaxException - -import okhttp3.Interceptor -import okhttp3.Response - -class ApiKeyAuth( - private val location: String = "", - private val paramName: String = "", - private var apiKey: String = "" -) : Interceptor { - - @Throws(IOException::class) - override fun intercept(chain: Interceptor.Chain): Response { - var request = chain.request() - - if ("query" == location) { - var newQuery = request.url.toUri().query - val paramValue = "$paramName=$apiKey" - if (newQuery == null) { - newQuery = paramValue - } else { - newQuery += "&$paramValue" - } - - val newUri: URI - try { - val oldUri = request.url.toUri() - newUri = URI(oldUri.scheme, oldUri.authority, - oldUri.path, newQuery, oldUri.fragment) - } catch (e: URISyntaxException) { - throw IOException(e) - } - - request = request.newBuilder().url(newUri.toURL()).build() - } else if ("header" == location) { - request = request.newBuilder() - .addHeader(paramName, apiKey) - .build() - } else if ("cookie" == location) { - request = request.newBuilder() - .addHeader("Cookie", "$paramName=$apiKey") - .build() - } - return chain.proceed(request) - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache deleted file mode 100644 index fc122514388c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBasicAuth.kt.mustache +++ /dev/null @@ -1,33 +0,0 @@ -package {{packageName}}.auth - -import java.io.IOException - -import kotlin.jvm.Throws -import okhttp3.Interceptor -import okhttp3.Interceptor.Chain -import okhttp3.Response -import okhttp3.Credentials - -class HttpBasicAuth( - private var username: String = "", - private var password: String = "" -) : Interceptor { - - fun setCredentials(username: String, password: String) { - this.username = username - this.password = password - } - - @Throws(IOException::class) - override fun intercept(chain: Chain): Response { - var request = chain.request() - - // If the request already have an authorization (eg. Basic auth), do nothing - if (request.header("Authorization") == null && username.isNotBlank() && password.isNotBlank()) { - request = request.newBuilder() - .addHeader("Authorization", Credentials.basic(username, password)) - .build() - } - return chain.proceed(request) - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache deleted file mode 100644 index 7488e6a9f573..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/HttpBearerAuth.kt.mustache +++ /dev/null @@ -1,39 +0,0 @@ -package {{packageName}}.auth - -import java.io.IOException - -import okhttp3.Interceptor -import okhttp3.Interceptor.Chain -import okhttp3.Response - -class HttpBearerAuth( - private var schema: String = "", - var bearerToken: String = "" -) : Interceptor { - - @Throws(IOException::class) - override fun intercept(chain: Chain): Response { - var request = chain.request() - - // If the request already have an authorization (eg. Basic auth), do nothing - if (request.header("Authorization") == null && bearerToken.isNotBlank()) { - request = request.newBuilder() - .addHeader("Authorization", headerValue()) - .build() - } - return chain.proceed(request) - } - - private fun headerValue(): String { - return if (schema.isNotBlank()) { - "${upperCaseBearer()} $bearerToken" - } else { - bearerToken - } - } - - private fun upperCaseBearer(): String { - return if (schema.lowercase().equals("bearer")) "Bearer" else schema - } - -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache deleted file mode 100644 index 8063b6c71861..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuth.kt.mustache +++ /dev/null @@ -1,151 +0,0 @@ -package {{packageName}}.auth - -import java.net.HttpURLConnection.HTTP_UNAUTHORIZED -import java.net.HttpURLConnection.HTTP_FORBIDDEN - -import java.io.IOException - -import org.apache.oltu.oauth2.client.OAuthClient -import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest -import org.apache.oltu.oauth2.client.request.OAuthClientRequest -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder -import org.apache.oltu.oauth2.common.exception.OAuthProblemException -import org.apache.oltu.oauth2.common.exception.OAuthSystemException -import org.apache.oltu.oauth2.common.message.types.GrantType -import org.apache.oltu.oauth2.common.token.BasicOAuthToken - -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.Response - -class OAuth( - client: OkHttpClient, - var tokenRequestBuilder: TokenRequestBuilder -) : Interceptor { - - interface AccessTokenListener { - fun notify(token: BasicOAuthToken) - } - - private var oauthClient: OAuthClient = OAuthClient(OAuthOkHttpClient(client)) - - @Volatile - private var accessToken: String? = null - var authenticationRequestBuilder: AuthenticationRequestBuilder? = null - private var accessTokenListener: AccessTokenListener? = null - - constructor( - requestBuilder: TokenRequestBuilder - ) : this( - OkHttpClient(), - requestBuilder - ) - - constructor( - flow: OAuthFlow, - authorizationUrl: String, - tokenUrl: String, - scopes: String - ) : this( - OAuthClientRequest.tokenLocation(tokenUrl).setScope(scopes) - ) { - setFlow(flow) - authenticationRequestBuilder = OAuthClientRequest.authorizationLocation(authorizationUrl) - } - - fun setFlow(flow: OAuthFlow) { - when (flow) { - OAuthFlow.accessCode, OAuthFlow.implicit -> - tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE) - OAuthFlow.password -> - tokenRequestBuilder.setGrantType(GrantType.PASSWORD) - OAuthFlow.application -> - tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS) - } - } - - @Throws(IOException::class) - override fun intercept(chain: Interceptor.Chain): Response { - return retryingIntercept(chain, true) - } - - @Throws(IOException::class) - private fun retryingIntercept(chain: Interceptor.Chain, updateTokenAndRetryOnAuthorizationFailure: Boolean): Response { - var request = chain.request() - - // If the request already have an authorization (eg. Basic auth), do nothing - if (request.header("Authorization") != null) { - return chain.proceed(request) - } - - // If first time, get the token - val oAuthRequest: OAuthClientRequest - if (accessToken == null) { - updateAccessToken(null) - } - - if (accessToken != null) { - // Build the request - val rb = request.newBuilder() - - val requestAccessToken = accessToken - try { - oAuthRequest = OAuthBearerClientRequest(request.url.toString()) - .setAccessToken(requestAccessToken) - .buildHeaderMessage() - } catch (e: OAuthSystemException) { - throw IOException(e) - } - - oAuthRequest.headers.entries.forEach { header -> - rb.addHeader(header.key, header.value) - } - rb.url(oAuthRequest.locationUri) - - //Execute the request - val response = chain.proceed(rb.build()) - - // 401/403 most likely indicates that access token has expired. Unless it happens two times in a row. - if ((response.code == HTTP_UNAUTHORIZED || response.code == HTTP_FORBIDDEN) && updateTokenAndRetryOnAuthorizationFailure) { - try { - if (updateAccessToken(requestAccessToken)) { - response.body?.close() - return retryingIntercept(chain, false) - } - } catch (e: Exception) { - response.body?.close() - throw e - } - } - return response - } else { - return chain.proceed(chain.request()) - } - } - - /** - * Returns true if the access token has been updated - */ - @Throws(IOException::class) - @Synchronized - fun updateAccessToken(requestAccessToken: String?): Boolean { - if (accessToken == null || accessToken.equals(requestAccessToken)) { - return try { - val accessTokenResponse = oauthClient.accessToken(this.tokenRequestBuilder.buildBodyMessage()) - if (accessTokenResponse != null && accessTokenResponse.accessToken != null) { - accessToken = accessTokenResponse.accessToken - accessTokenListener?.notify(accessTokenResponse.oAuthToken as BasicOAuthToken) - !accessToken.equals(requestAccessToken) - } else { - false - } - } catch (e: OAuthSystemException) { - throw IOException(e) - } catch (e: OAuthProblemException) { - throw IOException(e) - } - } - return true - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache deleted file mode 100644 index 05ad3f7e547b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthFlow.kt.mustache +++ /dev/null @@ -1,5 +0,0 @@ -package {{packageName}}.auth - -enum class OAuthFlow { - accessCode, implicit, password, application -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache deleted file mode 100644 index 0731e92a890c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/auth/OAuthOkHttpClient.kt.mustache +++ /dev/null @@ -1,61 +0,0 @@ -package {{packageName}}.auth - -import java.io.IOException - -import org.apache.oltu.oauth2.client.HttpClient -import org.apache.oltu.oauth2.client.request.OAuthClientRequest -import org.apache.oltu.oauth2.client.response.OAuthClientResponse -import org.apache.oltu.oauth2.client.response.OAuthClientResponseFactory -import org.apache.oltu.oauth2.common.exception.OAuthProblemException -import org.apache.oltu.oauth2.common.exception.OAuthSystemException - -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody - - -class OAuthOkHttpClient( - private var client: OkHttpClient = OkHttpClient() -) : HttpClient { - - @Throws(OAuthSystemException::class, OAuthProblemException::class) - override fun execute( - request: OAuthClientRequest, - headers: Map?, - requestMethod: String, - responseClass: Class?): T { - - var mediaType = "application/json".toMediaTypeOrNull() - val requestBuilder = Request.Builder().url(request.locationUri) - - headers?.forEach { entry -> - if (entry.key.equals("Content-Type", true)) { - mediaType = entry.value.toMediaTypeOrNull() - } else { - requestBuilder.addHeader(entry.key, entry.value) - } - } - - val body: RequestBody? = if (request.body != null) request.body.toRequestBody(mediaType) else null - requestBuilder.method(requestMethod, body) - - try { - val response = client.newCall(requestBuilder.build()).execute() - return OAuthClientResponseFactory.createCustomResponse( - response.body?.string(), - response.body?.contentType()?.toString(), - response.code, - response.headers.toMultimap(), - responseClass) - } catch (e: IOException) { - throw OAuthSystemException(e) - } - } - - override fun shutdown() { - // Nothing to do here - } - -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache deleted file mode 100644 index 15ec80cdbadd..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/bodyParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isBodyParam}}@Body {{{paramName}}}: {{{dataType}}}{{^required}}? = null{{/required}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache deleted file mode 100644 index 3aaa4e67fbf4..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/explodedQueryParam.mustache +++ /dev/null @@ -1 +0,0 @@ -@Query("{{baseName}}") {{{baseName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}{{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{{dataType}}}{{/collectionFormat}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache deleted file mode 100644 index fe5c9db69053..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/formParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isFormParam}}{{^isFile}}{{#isMultipart}}@Part{{/isMultipart}}{{^isMultipart}}@Field{{/isMultipart}}("{{baseName}}") {{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isFile}}{{#isFile}}{{#isMultipart}}@Part{{/isMultipart}}{{^isMultipart}}@Field("{{baseName}}"){{/isMultipart}} {{{paramName}}}: {{#isCollectionFormatMulti}}List<{{/isCollectionFormatMulti}}MultipartBody.Part{{#isCollectionFormatMulti}}>{{/isCollectionFormatMulti}}{{^required}}? = null{{/required}}{{/isFile}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache deleted file mode 100644 index 53f81b071750..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/headerParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isHeaderParam}}@Header("{{baseName}}") {{{paramName}}}: {{#isEnum}}{{enumName}}{{operationIdCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index a10257b657f5..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,412 +0,0 @@ -package {{packageName}}.infrastructure - -{{#hasOAuthMethods}} -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.AuthenticationRequestBuilder -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder -import {{packageName}}.auth.OAuth -import {{packageName}}.auth.OAuth.AccessTokenListener -import {{packageName}}.auth.OAuthFlow -{{/hasOAuthMethods}} -{{#hasAuthMethods}} -{{#authMethods}} -{{#isBasic}} -{{#isBasicBasic}} -import {{packageName}}.auth.HttpBasicAuth -{{/isBasicBasic}} -{{#isBasicBearer}} -import {{packageName}}.auth.HttpBearerAuth -{{/isBasicBearer}} -{{/isBasic}} -{{#isApiKey}} -import {{packageName}}.auth.ApiKeyAuth -{{/isApiKey}} -{{/authMethods}} -{{/hasAuthMethods}} - -import okhttp3.Call -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import retrofit2.Retrofit -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Converter -import retrofit2.CallAdapter -import retrofit2.converter.scalars.ScalarsConverterFactory -{{#useRxJava}} -import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory -{{/useRxJava}} -{{#useRxJava2}} -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -{{/useRxJava2}} -{{#useRxJava3}} -import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory -{{/useRxJava3}} -{{#gson}} -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import retrofit2.converter.gson.GsonConverterFactory -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Moshi -import retrofit2.converter.moshi.MoshiConverterFactory -{{/moshi}} -{{#jackson}} -import com.fasterxml.jackson.databind.ObjectMapper -import retrofit2.converter.jackson.JacksonConverterFactory -{{/jackson}} - -{{#kotlinx_serialization}} -import retrofit2.converter.kotlinx.serialization.asConverterFactory -import {{packageName}}.infrastructure.Serializer.kotlinxSerializationJson -import okhttp3.MediaType.Companion.toMediaType -{{/kotlinx_serialization}} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class ApiClient( - private var baseUrl: String = defaultBasePath, - private val okHttpClientBuilder: OkHttpClient.Builder? = null, - {{^kotlinx_serialization}} - private val serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = {{#generateOneOfAnyOfWrappers}}{{#gson}}registerTypeAdapterFactoryForAllModels({{/gson}}{{/generateOneOfAnyOfWrappers}}Serializer.{{#gson}}gsonBuilder{{/gson}}{{#generateOneOfAnyOfWrappers}}{{#gson}}){{/gson}}{{/generateOneOfAnyOfWrappers}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}}, - {{/kotlinx_serialization}} - private val callFactory: Call.Factory? = null, - private val callAdapterFactories: List = listOf( - {{#useRxJava}} - RxJavaCallAdapterFactory.create(), - {{/useRxJava}} - {{#useRxJava2}} - RxJava2CallAdapterFactory.create(), - {{/useRxJava2}} - {{#useRxJava3}} - RxJava3CallAdapterFactory.create(), - {{/useRxJava3}} - ), - private val converterFactories: List = listOf( - ScalarsConverterFactory.create(), - {{#gson}} - GsonConverterFactory.create(serializerBuilder.create()), - {{/gson}} - {{#moshi}} - MoshiConverterFactory.create(serializerBuilder.build()), - {{/moshi}} - {{#kotlinx_serialization}} - kotlinxSerializationJson.asConverterFactory("application/json".toMediaType()), - {{/kotlinx_serialization}} - {{#jackson}} - JacksonConverterFactory.create(serializerBuilder), - {{/jackson}} - ) -) { - private val apiAuthorizations = mutableMapOf() - var logger: ((String) -> Unit)? = null - - private val retrofitBuilder: Retrofit.Builder by lazy { - Retrofit.Builder() - .baseUrl(baseUrl) - .apply { - callAdapterFactories.forEach { - addCallAdapterFactory(it) - } - } - .apply { - converterFactories.forEach { - addConverterFactory(it) - } - } - } - - private val clientBuilder: OkHttpClient.Builder by lazy { - okHttpClientBuilder ?: defaultClientBuilder - } - - private val defaultClientBuilder: OkHttpClient.Builder by lazy { - OkHttpClient() - .newBuilder() - .addInterceptor(HttpLoggingInterceptor { message -> logger?.invoke(message) } - .apply { level = HttpLoggingInterceptor.Level.BODY } - ) - } - - init { - normalizeBaseUrl() - } - - {{#hasAuthMethods}} - constructor( - baseUrl: String = defaultBasePath, - okHttpClientBuilder: OkHttpClient.Builder? = null, - {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} - authNames: Array - ) : this(baseUrl, okHttpClientBuilder{{^kotlinx_serialization}}, serializerBuilder{{/kotlinx_serialization}}) { - authNames.forEach { authName -> - val auth: Interceptor? = when (authName) { {{#authMethods}} - {{#isBasicBasic}}"{{name}}" -> HttpBasicAuth() - {{/isBasicBasic}}{{#isBasicBearer}}"{{name}}" -> HttpBearerAuth("{{scheme}}") - {{/isBasicBearer}}{{#isApiKey}}"{{name}}" -> ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}") - {{/isApiKey}}{{#isOAuth}}"{{name}}" -> OAuth(OAuthFlow.{{flow}}, "{{authorizationUrl}}", "{{tokenUrl}}", "{{#scopes}}{{scope}}{{^-last}}, {{/-last}}{{/scopes}}") - {{/isOAuth}}{{^isBasicBasic}}{{^isBasicBearer}}{{^isApiKey}}{{^isOAuth}}"{{name}}" -> null{{/isOAuth}}{{/isApiKey}}{{/isBasicBearer}}{{/isBasicBasic}}{{/authMethods}} - else -> throw RuntimeException("auth name $authName not found in available auth names") - } - if (auth != null) { - addAuthorization(authName, auth) - } - } - {{#generateOneOfAnyOfWrappers}} - {{^kotlinx_serialization}} - {{#gson}} - {{#models}} - {{#model}} - {{^isEnum}} - {{^hasChildren}} - serializerBuilder.registerTypeAdapterFactory({{modelPackage}}.{{{classname}}}.CustomTypeAdapterFactory()) - {{/hasChildren}} - {{/isEnum}} - {{/model}} - {{/models}} - {{/gson}} - {{/kotlinx_serialization}} - {{/generateOneOfAnyOfWrappers}} - } - - {{#authMethods}} - {{#isBasic}} - {{#isBasicBasic}} - constructor( - baseUrl: String = defaultBasePath, - okHttpClientBuilder: OkHttpClient.Builder? = null, - {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} - authName: String, - username: String, - password: String - ) : this(baseUrl, okHttpClientBuilder, {{^kotlinx_serialization}}serializerBuilder, {{/kotlinx_serialization}}arrayOf(authName)) { - setCredentials(username, password) - } - - {{/isBasicBasic}} - {{#isBasicBearer}} - constructor( - baseUrl: String = defaultBasePath, - okHttpClientBuilder: OkHttpClient.Builder? = null, - {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} - authName: String, - bearerToken: String - ) : this(baseUrl, okHttpClientBuilder, {{^kotlinx_serialization}}serializerBuilder, {{/kotlinx_serialization}}arrayOf(authName)) { - setBearerToken(bearerToken) - } - - {{/isBasicBearer}} - {{/isBasic}} - {{/authMethods}} - {{#hasOAuthMethods}} - constructor( - baseUrl: String = defaultBasePath, - okHttpClientBuilder: OkHttpClient.Builder? = null, - {{^kotlinx_serialization}}serializerBuilder: {{#gson}}GsonBuilder{{/gson}}{{#moshi}}Moshi.Builder{{/moshi}}{{#jackson}}ObjectMapper{{/jackson}} = Serializer.{{#gson}}gsonBuilder{{/gson}}{{#moshi}}moshiBuilder{{/moshi}}{{#jackson}}jacksonObjectMapper{{/jackson}},{{/kotlinx_serialization}} - authName: String, - clientId: String, - secret: String, - username: String, - password: String - ) : this(baseUrl, okHttpClientBuilder, {{^kotlinx_serialization}}serializerBuilder, {{/kotlinx_serialization}}arrayOf(authName)) { - getTokenEndPoint() - ?.setClientId(clientId) - ?.setClientSecret(secret) - ?.setUsername(username) - ?.setPassword(password) - } - - {{/hasOAuthMethods}} - {{#authMethods}} - {{#isBasic}} - {{#isBasicBasic}} - fun setCredentials(username: String, password: String): ApiClient { - apiAuthorizations.values.runOnFirst { - setCredentials(username, password) - } - {{#hasOAuthMethods}} - apiAuthorizations.values.runOnFirst { - tokenRequestBuilder.setUsername(username).setPassword(password) - } - {{/hasOAuthMethods}} - return this - } - - {{/isBasicBasic}} - {{^isBasicBasic}} - {{#hasOAuthMethods}} - fun setCredentials(username: String, password: String): ApiClient { - apiAuthorizations.values.runOnFirst { - tokenRequestBuilder.setUsername(username).setPassword(password) - } - return this - } - {{/hasOAuthMethods}} - {{/isBasicBasic}} - {{#isBasicBearer}} - fun setBearerToken(bearerToken: String): ApiClient { - apiAuthorizations.values.runOnFirst { - this.bearerToken = bearerToken - } - return this - } - - {{/isBasicBearer}} - {{/isBasic}} - {{/authMethods}} - {{/hasAuthMethods}} - {{#hasOAuthMethods}} - /** - * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) - * @return Token request builder - */ - fun getTokenEndPoint(): TokenRequestBuilder? { - var result: TokenRequestBuilder? = null - apiAuthorizations.values.runOnFirst { - result = tokenRequestBuilder - } - return result - } - - /** - * Helper method to configure authorization endpoint of the first oauth found in the apiAuthorizations (there should be only one) - * @return Authentication request builder - */ - fun getAuthorizationEndPoint(): AuthenticationRequestBuilder? { - var result: AuthenticationRequestBuilder? = null - apiAuthorizations.values.runOnFirst { - result = authenticationRequestBuilder - } - return result - } - - /** - * Helper method to pre-set the oauth access token of the first oauth found in the apiAuthorizations (there should be only one) - * @param accessToken Access token - * @return ApiClient - */ - fun setAccessToken(accessToken: String): ApiClient { - apiAuthorizations.values.runOnFirst { - setAccessToken(accessToken) - } - return this - } - - /** - * Helper method to configure the oauth accessCode/implicit flow parameters - * @param clientId Client ID - * @param clientSecret Client secret - * @param redirectURI Redirect URI - * @return ApiClient - */ - fun configureAuthorizationFlow(clientId: String, clientSecret: String, redirectURI: String): ApiClient { - apiAuthorizations.values.runOnFirst { - tokenRequestBuilder - .setClientId(clientId) - .setClientSecret(clientSecret) - .setRedirectURI(redirectURI) - authenticationRequestBuilder - ?.setClientId(clientId) - ?.setRedirectURI(redirectURI) - } - return this - } - - /** - * Configures a listener which is notified when a new access token is received. - * @param accessTokenListener Access token listener - * @return ApiClient - */ - fun registerAccessTokenListener(accessTokenListener: AccessTokenListener): ApiClient { - apiAuthorizations.values.runOnFirst { - registerAccessTokenListener(accessTokenListener) - } - return this - } - - {{/hasOAuthMethods}} - /** - * Adds an authorization to be used by the client - * @param authName Authentication name - * @param authorization Authorization interceptor - * @return ApiClient - */ - fun addAuthorization(authName: String, authorization: Interceptor): ApiClient { - if (apiAuthorizations.containsKey(authName)) { - throw RuntimeException("auth name $authName already in api authorizations") - } - apiAuthorizations[authName] = authorization - clientBuilder.addInterceptor(authorization) - return this - } - - fun setLogger(logger: (String) -> Unit): ApiClient { - this.logger = logger - return this - } - - fun createService(serviceClass: Class): S { - val usedCallFactory = this.callFactory ?: clientBuilder.build() - return retrofitBuilder.callFactory(usedCallFactory).build().create(serviceClass) - } - - {{#generateOneOfAnyOfWrappers}} - {{^kotlinx_serialization}} - {{#gson}} - /** - * Gets the serializer builder. - * @return serial builder - */ - fun getSerializerBuilder(): GsonBuilder { - return serializerBuilder - } - - {{/gson}} - {{/kotlinx_serialization}} - {{/generateOneOfAnyOfWrappers}} - private fun normalizeBaseUrl() { - if (!baseUrl.endsWith("/")) { - baseUrl += "/" - } - } - - private inline fun Iterable.runOnFirst(callback: U.() -> Unit) { - for (element in this) { - if (element is U) { - callback.invoke(element) - break - } - } - } - - companion object { - @JvmStatic - protected val baseUrlKey = "{{packageName}}.baseUrl" - - @JvmStatic - val defaultBasePath: String by lazy { - System.getProperties().getProperty(baseUrlKey, "{{{basePath}}}") - } - } -} -{{#generateOneOfAnyOfWrappers}} -{{^kotlinx_serialization}} -{{#gson}} - -/** - * Registers all models with the type adapter factory. - * - * @param gsonBuilder gson builder - * @return GSON builder - */ -fun registerTypeAdapterFactoryForAllModels(gsonBuilder: GsonBuilder): GsonBuilder { - {{#models}} - {{#model}} - {{^isEnum}} - {{^hasChildren}} - gsonBuilder.registerTypeAdapterFactory({{modelPackage}}.{{{classname}}}.CustomTypeAdapterFactory()) - {{/hasChildren}} - {{/isEnum}} - {{/model}} - {{/models}} - return gsonBuilder -} -{{/gson}} -{{/kotlinx_serialization}} -{{/generateOneOfAnyOfWrappers}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache deleted file mode 100644 index 4e8030cb56f3..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/CollectionFormats.kt.mustache +++ /dev/null @@ -1,56 +0,0 @@ -package {{packageName}}.infrastructure - -class CollectionFormats { - - open class CSVParams { - - var params: List - - constructor(params: List) { - this.params = params - } - - constructor(vararg params: String) { - this.params = listOf(*params) - } - - override fun toString(): String { - return params.joinToString(",") - } - } - - open class SSVParams : CSVParams { - - constructor(params: List) : super(params) - - constructor(vararg params: String) : super(*params) - - override fun toString(): String { - return params.joinToString(" ") - } - } - - class TSVParams : CSVParams { - - constructor(params: List) : super(params) - - constructor(vararg params: String) : super(*params) - - override fun toString(): String { - return params.joinToString("\t") - } - } - - class PIPESParams : CSVParams { - - constructor(params: List) : super(params) - - constructor(vararg params: String) : super(*params) - - override fun toString(): String { - return params.joinToString("|") - } - } - - class SPACEParams : SSVParams() -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache deleted file mode 100644 index a920e0482022..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/infrastructure/ResponseExt.kt.mustache +++ /dev/null @@ -1,35 +0,0 @@ -package {{packageName}}.infrastructure - -{{#moshi}} -import com.squareup.moshi.JsonDataException -import com.squareup.moshi.Moshi -{{/moshi}} -{{#gson}} -import com.google.gson.GsonBuilder -import com.google.gson.JsonParseException -{{/gson}} -import retrofit2.Response - -{{#moshi}} -@Throws(JsonDataException::class) -inline fun Response<*>.getErrorResponse(serializerBuilder: Moshi.Builder = Serializer.moshiBuilder): T? { - val serializer = serializerBuilder.build() - val parser = serializer.adapter(T::class.java) - val response = errorBody()?.string() - if (response != null) { - return parser.fromJson(response) - } - return null -} -{{/moshi}} -{{#gson}} -@Throws(JsonParseException::class) -inline fun Response<*>.getErrorResponse(serializerBuilder: GsonBuilder = Serializer.gsonBuilder): T? { - val serializer = serializerBuilder.create() - val reader = errorBody()?.charStream() - if (reader != null) { - return serializer.fromJson(reader, T::class.java) - } - return null -} -{{/gson}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache deleted file mode 100644 index fe821c41b4df..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/paramJavadoc.mustache +++ /dev/null @@ -1,5 +0,0 @@ -{{#allParams}}{{#isDeepObject}} - * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/isDeepObject}}{{^isDeepObject}}{{#isExplode}}{{#hasVars}}{{#vars}} - * @param {{{baseName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/vars}}{{/hasVars}}{{^hasVars}} - * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/hasVars}}{{/isExplode}}{{^isExplode}} - * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}{{/isExplode}}{{/isDeepObject}}{{/allParams}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache deleted file mode 100644 index 685c514e6a90..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/pathParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isPathParam}}@Path("{{baseName}}") {{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache deleted file mode 100644 index 86ece566a3c2..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParam.mustache +++ /dev/null @@ -1 +0,0 @@ -@Query("{{baseName}}") {{{paramName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}@JvmSuppressWildcards {{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{/collectionFormat}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache deleted file mode 100644 index ab229cf40e47..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-retrofit2/queryParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isQueryParam}}{{#isDeepObject}}{{>queryParam}}{{/isDeepObject}}{{^isDeepObject}}{{#isExplode}}{{#hasVars}}{{#vars}}{{>explodedQueryParam}}{{^-last}}, {{/-last}}{{/vars}}{{/hasVars}}{{^hasVars}}{{>queryParam}}{{/hasVars}}{{/isExplode}}{{^isExplode}}{{>queryParam}}{{/isExplode}}{{/isDeepObject}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache deleted file mode 100644 index 1c533aae70a5..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/api.mustache +++ /dev/null @@ -1,147 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -{{#jackson}} -import com.fasterxml.jackson.annotation.JsonProperty -{{/jackson}} - -import org.springframework.web.client.RestClient -import org.springframework.web.client.RestClientResponseException - -{{#jackson}} -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter -{{/jackson}} -import org.springframework.http.ResponseEntity -import org.springframework.http.MediaType - - -{{#imports}}import {{import}} -{{/imports}} -import {{packageName}}.infrastructure.* - -{{#operations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(client: RestClient) : ApiClient(client) { - - {{#jackson}} - constructor(baseUrl: String) : this(RestClient.builder() - .baseUrl(baseUrl) - .messageConverters { it.add(MappingJackson2HttpMessageConverter()) } - .build() - ) - {{/jackson}} - - {{#operation}} - {{#allParams}} - {{#isEnum}} - /** - * enum for parameter {{paramName}} - */ - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{^enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/jackson}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - {{#enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^-last}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/jackson}} - {{/-last}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - } - - {{/isEnum}} - {{/allParams}} - - @Throws(RestClientResponseException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} { - {{#returnType}}val result = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - {{#returnType}} - return result.body!! - {{/returnType}} - } - - @Throws(RestClientResponseException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): ResponseEntity<{{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { - val localVariableConfig = {{operationId}}RequestConfig({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - return request<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}, {{{returnType}}}{{^returnType}}Unit{{/returnType}}>( - localVariableConfig - ) - } - - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}RequestConfig({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : RequestConfig<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}> { - val localVariableBody = {{#hasBodyParam}}{{! - }}{{#bodyParams}}{{{paramName}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{! - }}{{^hasFormParams}}null{{/hasFormParams}}{{! - }}{{#hasFormParams}}mapOf({{#formParams}} - "{{{baseName}}}" to PartConfig(body = {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}, headers = mutableMapOf({{#contentType}}"Content-Type" to "{{contentType}}"{{/contentType}})),{{! - }}{{/formParams}}){{/hasFormParams}}{{! - }}{{/hasBodyParam}} - val localVariableQuery = {{^hasQueryParams}}mutableMapOf>() -{{/hasQueryParams}}{{#hasQueryParams}}mutableMapOf>() - .apply { - {{#queryParams}} - {{^required}} - if ({{{paramName}}} != null) { - put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) - } - {{/required}} - {{#required}} - {{#isNullable}} - if ({{{paramName}}} != null) { - put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) - } - {{/isNullable}} - {{^isNullable}} - put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) - {{/isNullable}} - {{/required}} - {{/queryParams}} - } - {{/hasQueryParams}} - val localVariableHeaders: MutableMap = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}}) - {{#headerParams}} - {{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } - {{/headerParams}} - {{^hasFormParams}}{{#hasConsumes}}{{#consumes}}localVariableHeaders["Content-Type"] = "{{{mediaType}}}" - {{/consumes}}{{/hasConsumes}}{{/hasFormParams}}{{#hasProduces}}localVariableHeaders["Accept"] = "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}" -{{/hasProduces}} - - val params = mutableMapOf( - {{#pathParams}} - "{{baseName}}" to {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}{{/isContainer}}, - {{/pathParams}} - ) - - return RequestConfig( - method = RequestMethod.{{httpMethod}}, - path = "{{path}}", - params = params, - query = localVariableQuery, - headers = localVariableHeaders, - requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, - body = localVariableBody - ) - } - - {{/operation}} -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index 9d116ed9b175..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-restclient/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,68 +0,0 @@ -package {{packageName}}.infrastructure; - -import org.springframework.core.ParameterizedTypeReference -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpMethod -import org.springframework.http.MediaType -import org.springframework.web.client.RestClient -import org.springframework.http.ResponseEntity -import org.springframework.util.LinkedMultiValueMap - -open class ApiClient(protected val client: RestClient) { - - protected inline fun request(requestConfig: RequestConfig): ResponseEntity { - return prepare(defaults(requestConfig)) - .retrieve() - .toEntity(object : ParameterizedTypeReference() {}) - } - - protected fun prepare(requestConfig: RequestConfig) = - client.method(requestConfig) - .uri(requestConfig) - .headers(requestConfig) - .nullableBody(requestConfig) - - protected fun defaults(requestConfig: RequestConfig) = - requestConfig.apply { - if (body != null && headers[HttpHeaders.CONTENT_TYPE].isNullOrEmpty()) { - headers[HttpHeaders.CONTENT_TYPE] = MediaType.APPLICATION_JSON_VALUE - } - if (headers[HttpHeaders.ACCEPT].isNullOrEmpty()) { - headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE - } - } - - private fun RestClient.method(requestConfig: RequestConfig)= - method(HttpMethod.valueOf(requestConfig.method.name)) - - private fun RestClient.RequestBodyUriSpec.uri(requestConfig: RequestConfig) = - uri { builder -> - builder - .path(requestConfig.path) - .queryParams(LinkedMultiValueMap(requestConfig.query)) - .build(requestConfig.params) - } - - private fun RestClient.RequestBodySpec.headers(requestConfig: RequestConfig) = - apply { requestConfig.headers.forEach { (name, value) -> header(name, value) } } - - private fun RestClient.RequestBodySpec.nullableBody(requestConfig: RequestConfig) = - apply { if (requestConfig.body != null) body(requestConfig.body) } -} - -inline fun parseDateToQueryString(value : T): String { - {{#toJson}} - /* - .replace("\"", "") converts the json object string to an actual string for the query parameter. - The moshi or gson adapter allows a more generic solution instead of trying to use a native - formatter. It also easily allows to provide a simple way to define a custom date format pattern - inside a gson/moshi adapter. - */ - {{#jackson}} - return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") - {{/jackson}} - {{/toJson}} - {{^toJson}} - return value.toString() - {{/toJson}} - } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache deleted file mode 100644 index 556397463513..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/api.mustache +++ /dev/null @@ -1,149 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -{{#jackson}} -import com.fasterxml.jackson.annotation.JsonProperty -{{/jackson}} - -import org.springframework.web.reactive.function.client.WebClient -import org.springframework.web.reactive.function.client.WebClientResponseException -{{#jackson}} -import org.springframework.http.codec.json.Jackson2JsonDecoder -import org.springframework.http.codec.json.Jackson2JsonEncoder -{{/jackson}} -import org.springframework.http.ResponseEntity -import org.springframework.http.MediaType -import reactor.core.publisher.Mono -import org.springframework.util.LinkedMultiValueMap - -{{#imports}}import {{import}} -{{/imports}} -import {{packageName}}.infrastructure.* - -{{#operations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(client: WebClient) : ApiClient(client) { - - {{#jackson}} - constructor(baseUrl: String) : this(WebClient.builder() - .baseUrl(baseUrl) - .codecs { - it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(Serializer.jacksonObjectMapper, MediaType.APPLICATION_JSON)) - it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(Serializer.jacksonObjectMapper, MediaType.APPLICATION_JSON)) - } - .build() - ) - {{/jackson}} - - {{#operation}} - {{#allParams}} - {{#isEnum}} - /** - * enum for parameter {{paramName}} - */ - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{^enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/jackson}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - {{#enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^-last}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/jackson}} - {{/-last}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - } - - {{/isEnum}} - {{/allParams}} - - @Throws(WebClientResponseException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Mono<{{#returnType}}{{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { - return {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - .map { {{#returnType}}it.body{{/returnType}}{{^returnType}}Unit{{/returnType}} } - } - - @Throws(WebClientResponseException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): Mono> { - val localVariableConfig = {{operationId}}RequestConfig({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) - return request<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}, {{{returnType}}}{{^returnType}}Unit{{/returnType}}>( - localVariableConfig - ) - } - - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}RequestConfig({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{#defaultValue}} = {{>param_default_value}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : RequestConfig<{{#hasBodyParam}}{{#bodyParams}}{{{dataType}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}Unit{{/hasFormParams}}{{#hasFormParams}}Map>{{/hasFormParams}}{{/hasBodyParam}}> { - val localVariableBody = {{#hasBodyParam}}{{! - }}{{#bodyParams}}{{{paramName}}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{! - }}{{^hasFormParams}}null{{/hasFormParams}}{{! - }}{{#hasFormParams}}mapOf({{#formParams}} - "{{{baseName}}}" to PartConfig(body = {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}, headers = mutableMapOf({{#contentType}}"Content-Type" to "{{contentType}}"{{/contentType}})),{{! - }}{{/formParams}}){{/hasFormParams}}{{! - }}{{/hasBodyParam}} - val localVariableQuery = {{^hasQueryParams}}mutableMapOf>() -{{/hasQueryParams}}{{#hasQueryParams}}mutableMapOf>() - .apply { - {{#queryParams}} - {{^required}} - if ({{{paramName}}} != null) { - put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) - } - {{/required}} - {{#required}} - {{#isNullable}} - if ({{{paramName}}} != null) { - put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) - } - {{/isNullable}} - {{^isNullable}} - put("{{baseName}}", {{#isContainer}}toMultiValue({{{paramName}}}.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString({{{paramName}}}){{/isDateTime}}{{#isDate}}parseDateToQueryString({{{paramName}}}){{/isDate}}{{^isDateTime}}{{^isDate}}{{{paramName}}}.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) - {{/isNullable}} - {{/required}} - {{/queryParams}} - } - {{/hasQueryParams}} - val localVariableHeaders: MutableMap = mutableMapOf({{#hasFormParams}}"Content-Type" to {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{/hasFormParams}}) - {{#headerParams}} - {{{paramName}}}{{^required}}?{{/required}}.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} } - {{/headerParams}} - {{^hasFormParams}}{{#hasConsumes}}{{#consumes}}localVariableHeaders["Content-Type"] = "{{{mediaType}}}" - {{/consumes}}{{/hasConsumes}}{{/hasFormParams}}{{#hasProduces}}localVariableHeaders["Accept"] = "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}" -{{/hasProduces}} - - val params = mutableMapOf( - {{#pathParams}} - "{{baseName}}" to {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}{{/isContainer}}, - {{/pathParams}} - ) - - return RequestConfig( - method = RequestMethod.{{httpMethod}}, - path = "{{path}}", - params = params, - query = localVariableQuery, - headers = localVariableHeaders, - requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, - body = localVariableBody - ) - } - - {{/operation}} -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index 3042e1aecea6..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-spring-webclient/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,69 +0,0 @@ -package {{packageName}}.infrastructure; - -import org.springframework.core.ParameterizedTypeReference -import org.springframework.http.HttpHeaders -import org.springframework.http.HttpMethod -import org.springframework.http.MediaType -import org.springframework.web.reactive.function.client.WebClient -import org.springframework.http.ResponseEntity -import org.springframework.util.LinkedMultiValueMap -import reactor.core.publisher.Mono - -open class ApiClient(protected val client: WebClient) { - - protected inline fun request(requestConfig: RequestConfig): Mono> { - return prepare(defaults(requestConfig)) - .retrieve() - .toEntity(object : ParameterizedTypeReference() {}) - } - - protected fun prepare(requestConfig: RequestConfig) = - client.method(requestConfig) - .uri(requestConfig) - .headers(requestConfig) - .body(requestConfig) - - protected fun defaults(requestConfig: RequestConfig) = - requestConfig.apply { - if (body != null && headers[HttpHeaders.CONTENT_TYPE].isNullOrEmpty()) { - headers[HttpHeaders.CONTENT_TYPE] = MediaType.APPLICATION_JSON_VALUE - } - if (headers[HttpHeaders.ACCEPT].isNullOrEmpty()) { - headers[HttpHeaders.ACCEPT] = MediaType.APPLICATION_JSON_VALUE - } - } - - private fun WebClient.method(requestConfig: RequestConfig)= - method(HttpMethod.valueOf(requestConfig.method.name)) - - private fun WebClient.RequestBodyUriSpec.uri(requestConfig: RequestConfig) = - uri { builder -> - builder - .path(requestConfig.path) - .queryParams(LinkedMultiValueMap(requestConfig.query)) - .build(requestConfig.params) - } - - private fun WebClient.RequestBodySpec.headers(requestConfig: RequestConfig) = - apply { requestConfig.headers.forEach { (name, value) -> header(name, value) } } - - private fun WebClient.RequestBodySpec.body(requestConfig: RequestConfig) = - apply { if (requestConfig.body != null) bodyValue(requestConfig.body) } -} - -inline fun parseDateToQueryString(value : T): String { - {{#toJson}} - /* - .replace("\"", "") converts the json object string to an actual string for the query parameter. - The moshi or gson adapter allows a more generic solution instead of trying to use a native - formatter. It also easily allows to provide a simple way to define a custom date format pattern - inside a gson/moshi adapter. - */ - {{#jackson}} - return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") - {{/jackson}} - {{/toJson}} - {{^toJson}} - return value.toString() - {{/toJson}} - } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache deleted file mode 100644 index c84f15f0b3fb..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/api.mustache +++ /dev/null @@ -1,232 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -import java.io.IOException - -{{#imports}}import {{import}} -{{/imports}} - -{{#jackson}} -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.core.type.TypeReference -{{/jackson}} -{{#gson}} -import com.google.gson.reflect.TypeToken -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -{{/moshi}} - -import io.vertx.core.Vertx -import io.vertx.core.http.RequestOptions -import io.vertx.core.http.HttpMethod -import io.vertx.core.buffer.Buffer -import io.vertx.core.Future -import io.vertx.ext.web.client.WebClient -import io.vertx.uritemplate.UriTemplate - -{{#useCoroutines}} -import io.vertx.kotlin.coroutines.await -import io.vertx.kotlin.coroutines.dispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -{{/useCoroutines}} - -import {{packageName}}.infrastructure.* - -@Suppress ("UNUSED") -{{#operations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}class {{classname}}(basePath: kotlin.String = ApiClient.defaultBasePath, accessToken: String? = null, apiKey: MutableMap = mutableMapOf(), apiKeyPrefix: MutableMap = mutableMapOf(), username: String? = null, password: String? = null, vertx: Vertx): ApiClient(basePath, accessToken, apiKey, apiKeyPrefix, username, password, vertx) { - {{#operation}} - {{#allParams}} - {{#isEnum}} - /** - * enum for parameter {{paramName}} - */ - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{^enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{#moshi}} - @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/moshi}} - {{#gson}} - @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/kotlinx_serialization}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - {{#enumUnknownDefaultCase}} - {{#allowableValues}} - {{#enumVars}} - {{^-last}} - {{#moshi}} - @Json(name = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/moshi}} - {{#gson}} - @SerializedName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/gson}} - {{#jackson}} - @JsonProperty(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/jackson}} - {{#kotlinx_serialization}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) {{&name}}({{{value}}}), - {{/kotlinx_serialization}} - {{/-last}} - {{/enumVars}} - {{/allowableValues}} - {{/enumUnknownDefaultCase}} - } - - {{/isEnum}} - {{/allParams}} - /** - * {{summary}} - * {{notes}} - {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{#nullableReturnType}}{{^isResponseOptional}} or null{{/isResponseOptional}}{{/nullableReturnType}}{{#isResponseOptional}} or null{{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}} - * @throws IllegalStateException If the request is not correctly configured - * @throws IOException Rethrows the OkHttp execute method exception - * @throws UnsupportedOperationException If the API returns an informational or redirection response - * @throws ClientException If the API returns a client error response - * @throws ServerException If the API returns a server error response - */{{#returnType}} - @Suppress("UNCHECKED_CAST"){{/returnType}} - @Throws(IllegalStateException::class, IOException::class, UnsupportedOperationException::class, ClientException::class, ServerException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - {{#useCoroutines}}suspend {{/useCoroutines}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : {{^useCoroutines}}Future<{{/useCoroutines}}{{#returnType}}{{{returnType}}}{{#nullableReturnType}}{{^isResponseOptional}}?{{/isResponseOptional}}{{/nullableReturnType}}{{#isResponseOptional}}?{{/isResponseOptional}}{{/returnType}}{{^returnType}}Unit{{/returnType}}{{^useCoroutines}}>{{/useCoroutines}} { - return {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}} = {{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}).map { localVarResponse -> - when (localVarResponse.responseType) { - ResponseType.Success -> {{#returnType}}(localVarResponse as Success<*>).data as {{{returnType}}}{{#nullableReturnType}}{{^isResponseOptional}}?{{/isResponseOptional}}{{/nullableReturnType}}{{#isResponseOptional}}?{{/isResponseOptional}}{{/returnType}}{{^returnType}}Unit{{/returnType}} - ResponseType.Informational -> throw UnsupportedOperationException("Client does not support Informational responses.") - ResponseType.Redirection -> throw UnsupportedOperationException("Client does not support Redirection responses.") - ResponseType.ClientError -> { - val localVarError = localVarResponse as ClientError<*> - throw ClientException("Client error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) - } - ResponseType.ServerError -> { - val localVarError = localVarResponse as ServerError<*> - throw ServerException("Server error : ${localVarError.statusCode} ${localVarError.message.orEmpty()}", localVarError.statusCode, localVarResponse) - } - } - }{{#useCoroutines}}.await(){{/useCoroutines}} - } - - /** - * {{summary}} - * {{notes}} - {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}}* @return ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}> - * @throws IllegalStateException If the request is not correctly configured - * @throws IOException Rethrows the OkHttp execute method exception - */{{#returnType}} - @Suppress("UNCHECKED_CAST"){{/returnType}} - @Throws(IllegalStateException::class, IOException::class) - {{#isDeprecated}} - @Deprecated(message = "This operation is deprecated.") - {{/isDeprecated}} - fun {{operationId}}WithHttpInfo({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : Future> { - val vertxClient = WebClient.create(vertx) - val request = vertxClient.requestAbs(HttpMethod.{{httpMethod}}, UriTemplate.of("$basePath{{path}}"{{#pathParams}}.replace("{"+"{{baseName}}"+"}", encodeURIComponent({{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}.toString(){{/isContainer}})){{/pathParams}})) - - {{#hasFormParams}}request.putHeader("Content-Type", {{^consumes}}"multipart/form-data"{{/consumes}}{{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}){{/hasFormParams}} - {{#headerParams}}{{{paramName}}}{{^required}}?{{/required}}.apply { request.putHeader("{{baseName}}", {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}})}{{/headerParams}} - {{^hasFormParams}}{{#hasConsumes}} - {{#consumes}} - request.putHeader("Content-Type", "{{{mediaType}}}") - {{/consumes}} - {{/hasConsumes}}{{/hasFormParams}} - {{#hasProduces}}request.putHeader("Accept", "{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}"){{/hasProduces}} - - {{#hasFormParams}} - val form = io.vertx.core.MultiMap.caseInsensitiveMultiMap(); - {{#formParams}} - {{{paramName}}}{{^required}}?{{/required}}.let { form.add("{{{baseName}}}", {{{paramName}}}{{#isEnum}}{{^required}}?{{/required}}.value{{/isEnum}}{{^isString}}.toString(){{/isString}}) } - {{/formParams}} - {{/hasFormParams}} - - {{#hasQueryParams}} - {{#queryParams}} - {{{paramName}}}{{^required}}?{{/required}}.let { request.queryParams().add("{{baseName}}", {{#isContainer}}toMultiValue(it.toList(), "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{#isDateTime}}parseDateToQueryString(it){{/isDateTime}}{{#isDate}}parseDateToQueryString(it){{/isDate}}{{^isDateTime}}{{^isDate}}it.toString(){{/isDate}}{{/isDateTime}}){{/isContainer}}) } - {{/queryParams}} - {{/hasQueryParams}} - - {{#authMethods}} - {{#isApiKey}} - if (apiKey["{{keyParamName}}"] != null) { - if (apiKeyPrefix["{{keyParamName}}"] != null) { - {{#isKeyInHeader}} - request.putHeader("{{keyParamName}}", apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!) - {{/isKeyInHeader}} - {{#isKeyInQuery}} - request.queryParams().add("{{keyParamName}}", apiKeyPrefix["{{keyParamName}}"]!! + " " + apiKey["{{keyParamName}}"]!!) - {{/isKeyInQuery}} - } else { - {{#isKeyInHeader}} - request.putHeader("{{keyParamName}}", apiKey["{{keyParamName}}"]!!) - {{/isKeyInHeader}} - {{#isKeyInQuery}} - request.queryParams().add("{{keyParamName}}", apiKey["{{keyParamName}}"]!!) - {{/isKeyInQuery}} - } - } - {{/isApiKey}} - {{#isBasic}} - {{#isBasicBasic}} - username?.let { username -> - password?.let { password -> - request.basicAuthentication(username, password) - } - } - {{/isBasicBasic}} - {{#isBasicBearer}} - accessToken?.let { accessToken -> - request.bearerTokenAuthentication(accessToken) - } - {{/isBasicBearer}} - {{/isBasic}} - {{#isOAuth}} - accessToken?.let { accessToken -> - request.bearerTokenAuthentication(accessToken) - } - {{/isOAuth}} - {{/authMethods}} - - return request - {{#hasBodyParam}} - .sendBuffer(responseBody({{#bodyParams}}{{{paramName}}}{{/bodyParams}})) - {{/hasBodyParam}} - {{^hasBodyParam}} - .send() - {{/hasBodyParam}} - .map { - val apiResponse: ApiResponse<{{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit?{{/returnType}}> = handleResponse(it) - apiResponse - } - } - - {{/operation}} - - private inline fun responseBody(body: T): Buffer { - {{#moshi}} - return Buffer.buffer(Serializer.moshi.adapter(T::class.java).toJson(body)) - {{/moshi}} - {{#gson}} - return Buffer.buffer(Serializer.gson.toJson(body, T::class.java)) - {{/gson}} - {{#jackson}} - return Buffer.buffer(Serializer.jacksonObjectMapper.writeValueAsBytes(body)) - {{/jackson}} - } - -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index dacce6f4742a..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,110 +0,0 @@ -package {{packageName}}.infrastructure - -import io.vertx.core.Vertx -import io.vertx.core.buffer.Buffer -import java.nio.charset.StandardCharsets -{{#jackson}} -import com.fasterxml.jackson.core.type.TypeReference -{{/jackson}} -{{#gson}} -import com.google.gson.reflect.TypeToken -{{/gson}} - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient(val basePath: kotlin.String = defaultBasePath, val accessToken: String? = null, val apiKey: MutableMap = mutableMapOf(), val apiKeyPrefix: MutableMap = mutableMapOf(), var username: String? = null, var password: String? = null, val vertx: Vertx) { - companion object { - const val baseUrlKey = "{{packageName}}.baseUrl" - - @JvmStatic - val defaultBasePath: String by lazy { - System.getProperties().getProperty(baseUrlKey, "{{{basePath}}}") - } - } - - protected inline fun handleResponse(response: io.vertx.ext.web.client.HttpResponse): ApiResponse { - val code = response.statusCode() - val headers = response.headers().associate { it.key to listOf(it.value) } - val contentType = headers["Content-Type"]?.firstOrNull()?.substringBefore(";")?.lowercase(java.util.Locale.getDefault()) - - return when (code) { - in 100..199 -> Informational( - response.statusMessage(), - code, - headers - ) - in 200 .. 299 -> Success( - responseBody(response.body(), contentType), - code, - headers - ) - in 300..399 -> Redirection( - code, - headers - ) - in 400..499 -> ClientError( - response.statusMessage(), - response.bodyAsString(), - code, - headers - ) - else -> ServerError( - response.statusMessage(), - response.bodyAsString(), - code, - headers - ) - } - } - - protected inline fun responseBody(body: Buffer?, mediaType: String? = "application/json"): T? { - body ?: return null - - val bodyContent = String(body.bytes, StandardCharsets.UTF_8) - if (bodyContent.isEmpty()) { - return null - } - - return when { - mediaType==null || (mediaType.startsWith("application/") && mediaType.endsWith("json")) -> - {{#moshi}}Serializer.moshi.adapter(T::class.java).fromJson(bodyContent){{/moshi}}{{! - }}{{#gson}}Serializer.gson.fromJson(bodyContent, (object: TypeToken(){}).getType()){{/gson}}{{! - }}{{#jackson}}Serializer.jacksonObjectMapper.readValue(bodyContent, object: TypeReference() {}){{/jackson}}{{! - }}{{#kotlinx_serialization}}Serializer.kotlinxSerializationJson.decodeFromString(bodyContent){{/kotlinx_serialization}} - else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.") - } - } - - protected fun encodeURIComponent(parameter: String): String { - return try { - java.net.URLEncoder.encode(parameter, java.nio.charset.StandardCharsets.UTF_8.name()) - } catch (e: java.io.UnsupportedEncodingException) { - parameter - } - } - - protected inline fun parseDateToQueryString(value : T): String { - {{#toJson}} - /* - .replace("\"", "") converts the json object string to an actual string for the query parameter. - The moshi or gson adapter allows a more generic solution instead of trying to use a native - formatter. It also easily allows to provide a simple way to define a custom date format pattern - inside a gson/moshi adapter. - */ - {{#moshi}} - return Serializer.moshi.adapter(T::class.java).toJson(value).replace("\"", "") - {{/moshi}} - {{#gson}} - return Serializer.gson.toJson(value, T::class.java).replace("\"", "") - {{/gson}} - {{#jackson}} - return Serializer.jacksonObjectMapper.writeValueAsString(value).replace("\"", "") - {{/jackson}} - {{#kotlinx_serialization}} - return Serializer.kotlinxSerializationJson.encodeToString(value).replace("\"", "") - {{/kotlinx_serialization}} - {{/toJson}} - {{^toJson}} - return value.toString() - {{/toJson}} - } - -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache deleted file mode 100644 index d529ad5599f1..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/ApiResponse.kt.mustache +++ /dev/null @@ -1,43 +0,0 @@ -package {{packageName}}.infrastructure - -{{#nonPublicApi}}internal {{/nonPublicApi}}enum class ResponseType { - Success, Informational, Redirection, ClientError, ServerError -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}interface Response - -{{#nonPublicApi}}internal {{/nonPublicApi}}abstract class ApiResponse(val responseType: ResponseType): Response { - abstract val statusCode: Int - abstract val headers: Map> -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class Success( - val data: T{{#nullableReturnType}}?{{/nullableReturnType}}, - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -): ApiResponse(ResponseType.Success) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class Informational( - val statusText: String, - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -) : ApiResponse(ResponseType.Informational) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class Redirection( - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -) : ApiResponse(ResponseType.Redirection) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class ClientError( - val message: String? = null, - val body: Any? = null, - override val statusCode: Int = -1, - override val headers: Map> = mapOf() -) : ApiResponse(ResponseType.ClientError) - -{{#nonPublicApi}}internal {{/nonPublicApi}}class ServerError( - val message: String? = null, - val body: Any? = null, - override val statusCode: Int = -1, - override val headers: Map> -): ApiResponse(ResponseType.ServerError) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache deleted file mode 100644 index 1357da8d599c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-vertx/infrastructure/Errors.kt.mustache +++ /dev/null @@ -1,18 +0,0 @@ -@file:Suppress("unused") -package {{packageName}}.infrastructure - -import java.lang.RuntimeException - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ClientException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private const val serialVersionUID: Long = 123L - } -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ServerException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) { - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private const val serialVersionUID: Long = 456L - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache deleted file mode 100644 index 4aff1e8f1bc6..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/README.mustache +++ /dev/null @@ -1,230 +0,0 @@ -# {{packageName}} - Kotlin client library for {{appName}} - - -A kotlin client for Android using the currently recommended http client, Volley. See https://developer.android.com/training/volley - -- Currently sends GsonRequests -- Currently only supports Gson as a serializer - will throw an exception if a different serializer is chosen -- Defaults the source location to src/main/java as per standard Android builds - - -## Design - -Volley is a queue/request based layer on top of http url stack specific to Android. Android favours dependency injection and -a layered architecture, and IO performed off the main thread to maintain UI responsiveness, with a preferred technique of -kotlin co-routines. The code gen library reflects these factors. - -- Api calls use co-routines, and execute them using volley callbacks to avoid tying up a thread. -- Facilitate dependency injection, with default implementations available. -- Generate a requestFactory that can be overridden -- Allow the passing of the RequestFactory per tag (api client) or per operation (an extra parameter is created on operations with non-global security), with per operation auth overriding global security. -- DI scoping of the Request Factory and pre-generated auth header factories allow for thread safe and secure setting of credentials. -- Lazy header factories allow for refreshing tokens etc -- Factoring of header factories to the Request Factory allow ambient provision of credentials. Code gen library is credential storage agnostic. -- Header factories allow the merging of generated headers from open api spec with dynamically added headers - -- Injection of http url stack to allow custom http stacks. Default implementation is best practice singleton -- Data classes used for serialisation to reflect volley's preference - an immutable request that once queued can't be tampered with. - -- Reuse model class and other jvm common infrastructure - -- Optional generation of room database models, and transform methods to these from open api models -- Room and api models can be extended with additional extension properties. - -## Future improvements -- Option to generate image requests on certain conditionals e.g content-type gif etc -- Support for kotlin serialization. -- Multi part form parameters and support for file inputs - -## Usage -Hilt Dependency injection example - with default values for parameters overridden. -``` - @Provides - internal fun provideSomeApi( - context: Context, - restService: IRestService, - configurationService: IConfigurationService, - sessionService: ISessionService - ): SomeApi { - return SomeApi( - context = context, - requestQueue = restService.getRequestQueue(), - requestFactory = RequestFactory(listOf(createSessionHeaderFactory(sessionService), createTraceHeaderFactory()), - postProcessors = listOf(retryPolicySetter)), - basePath = configurationService.getBaseUrl() - ) - } -``` -Here is the constructor so you can see the defaults -```class SomeApi ( -val context: Context, -val requestQueue: Lazy = lazy(initializer = { - Volley.newRequestQueue(context.applicationContext) - }), - val requestFactory: IRequestFactory = RequestFactory(), - val basePath: String = "https://yourbasepath.from_input_parameter.com/api", - private val postProcessors :List <(Request<*>) -> Unit> = listOf()) { -``` - -### Overriding defaults -The above constructor for each api allows the following to be customized -- A custom context, so either a singleton request queue or different scope can be created - see -https://developer.android.com/training/volley/requestqueue#singleton -- An overridable request queue - which in turn can have a custom http url stack passed to it -- An overridable request factory constructor call, or a request factory that can be overridden by a custom template, with -custom header factory, request post processors and custom gson adapters injected. - -#### Overriding request generation -Request generation can be overridden by -- Overriding the entire request factory template -- Supplying custom header factories - methods that take any possible parameters but return a map of headers -- Supplying custom request post processors - methods that take and return the request object - -Header factory examples can be found in the auth section, as these are implemented as header factories. eg -``` -val basicAuthHeaderFactoryBuilder = { username: String?, password: String? -> -{ mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))} -} -``` -In this case it's a lambda function (a factory method) that takes an username and password, and returns a map of headers. Other -generated code will supply the username and password. In this case it results in a map of just one key/value pair, but -it could be multiple. The important part is it's returning a map - and that the surrounding code -will can bind the inputs to it at some point. - -Here is a different example that supplies tracing header values -``` -/** - * Create a lambda of tracing headers to be injected into an API's [RequestFactory]. - */ -private fun createTraceHeaderFactory(): () -> Map = { - mapOf( - HttpHeaderType.b3_traceId.rawValue to UUIDExtensions.asTraceId(UUID.randomUUID()), - HttpHeaderType.b3_spanId.rawValue to UUIDExtensions.asSpanId(UUID.randomUUID()), - HttpHeaderType.b3_sampled.rawValue to "1" - ) -} -``` -Finally a post processor example -``` - /** - * Configure a [DefaultRetryPolicy] to be injected into the [RequestFactory] with a maximum number of retries of zero. - */ - private val retryPolicySetter = { request: Request<*> -> - Unit.apply { - request.setRetryPolicy( - DefaultRetryPolicy( - RestService.DEFAULT_TIMEOUT_MS, - 0, - DefaultRetryPolicy.DEFAULT_BACKOFF_MULT - ) - ) - } - } -``` - -### Serialization -#### Gson and Polymorphic types -The GsonRequest object can be passed custom type adapters -``` -class GsonRequest( - method: Int, - url: String, - private val body: Any?, - private val headers: Map?, - private val params: MutableMap?, - private val contentTypeForBody: String?, - private val encodingForParams: String?, - private val gsonAdapters: Map?, - private val type: Type, - private val listener: Response.Listener, - errorListener: Response.ErrorListener -) : Request(method, url, errorListener) { - - val gsonBuilder: GsonBuilder = GsonBuilder() - .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) - .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter()) - .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) - .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) - -``` -## Requires - -{{#jvm}} -* Kotlin 1.4.30 -* Gradle 6.8.3 -{{/jvm}} -{{#multiplatform}} -* Kotlin 1.5.10 -{{/multiplatform}} - -## Build - -{{#jvm}} -First, create the gradle wrapper script: - -``` -gradle wrapper -``` - -Then, run: - -{{/jvm}} -``` -./gradlew check assemble -``` - -This runs all tests and packages the library. - -{{#generateApiDocs}} - -## Documentation for API Endpoints - -All URIs are relative to *{{{basePath}}}* - -Class | Method | HTTP request | Description ------------- | ------------- | ------------- | ------------- -{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}} -{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} -{{/generateApiDocs}} - -{{#generateModelDocs}} - -## Documentation for Models - -{{#modelPackage}} -{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md) -{{/model}}{{/models}} -{{/modelPackage}} -{{^modelPackage}} -No model defined in this package -{{/modelPackage}} -{{/generateModelDocs}} - - -## Documentation for Authorization - -{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} -{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} -{{#authMethods}} - -### {{name}} - -{{#isApiKey}}- **Type**: API key -- **API key parameter name**: {{keyParamName}} -- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} -{{/isApiKey}} -{{#isBasicBasic}}- **Type**: HTTP basic authentication -{{/isBasicBasic}} -{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} -{{/isBasicBearer}} -{{#isHttpSignature}}- **Type**: HTTP signature authentication -{{/isHttpSignature}} -{{#isOAuth}}- **Type**: OAuth -- **Flow**: {{flow}} -- **Authorization URL**: {{authorizationUrl}} -- **Scopes**: {{^scopes}}N/A{{/scopes}} -{{#scopes}} - {{scope}}: {{description}} -{{/scopes}} -{{/isOAuth}} - -{{/authMethods}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache deleted file mode 100644 index 515c3c6efbaf..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api.mustache +++ /dev/null @@ -1,116 +0,0 @@ -package {{apiPackage}} - -import android.content.Context -import com.android.volley.DefaultRetryPolicy -import com.android.volley.Request -import com.android.volley.RequestQueue -import com.android.volley.Response -import com.android.volley.toolbox.BaseHttpStack -import com.android.volley.toolbox.Volley -import java.util.* -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine -import com.google.gson.reflect.TypeToken - -import {{packageName}}.request.IRequestFactory -import {{packageName}}.request.RequestFactory -import {{packageName}}.infrastructure.CollectionFormats.* - -{{#imports}}import {{import}} -{{/imports}} - -{{#operations}} -/* -* If you wish to use a custom http stack with your client you -* can pass that to the request queue like: -* Volley.newRequestQueue(context.applicationContext, myCustomHttpStack) -*/ -class {{classname}} ( - private val context: Context, - private val requestQueue: Lazy = lazy(initializer = { - Volley.newRequestQueue(context.applicationContext) - }), - private val requestFactory: IRequestFactory = RequestFactory(), - private val basePath: String = "{{{basePath}}}", - private val postProcessors :List <(Request<*>) -> Unit> = listOf()) { - - {{#operation}} - /** - * {{summary}} - * {{notes}} - {{#allParams}}* @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} - */ - {{#isDeprecated}} - @Deprecated("This api was deprecated") - {{/isDeprecated}} - suspend fun {{operationId}}({{^allParams}}){{/allParams}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{#-last}}{{#operationAuthMethod}}, opAuthHeaderFactory = () -> map{{/operationAuthMethod}}){{/-last}}{{/allParams}}: {{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit{{/returnType}} { - {{#bodyParam}} - val body: Any? = {{paramName}} - {{/bodyParam}} - {{^bodyParam}} - val body: Any? = null - {{/bodyParam}} - - val contentTypes : Array = arrayOf({{#consumes}}"{{{mediaType}}}"{{^-last}},{{/-last}}{{/consumes}}) - val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" } - - // Do some work or avoid some work based on what we know about the model, - // before we delegate to a pluggable request factory template - // The request factory template contains only pure code and no templates - // to make it easy to override with your own. - - // create path and map variables - val path = "{{{path}}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}IRequestFactory.escapeString({{{paramName}}}.toString()){{/isContainer}}){{/pathParams}} - - val formParams = mapOf({{^formParams}}){{/formParams}}{{#formParams}} - "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}){{^-last}},{{/-last}}{{#-last}} - ){{/-last}}{{/formParams}} - - - // TODO: Cater for allowing empty values - // TODO, if its apikey auth, then add the header names here and the hardcoded auth key - // Only support hard coded apikey in query param auth for when we do this first path - val queryParams = mapOf({{^queryParams}}){{/queryParams}}{{#queryParams}} - "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}){{^-last}},{{/-last}}{{#-last}} - ){{/-last}}{{/queryParams}} - .filter { it.value.isNotEmpty() } - - val headerParams: Map = mapOf({{^headerParams}}){{/headerParams}}{{#headerParams}} - "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}){{^-last}},{{/-last}}{{#-last}} - ){{/-last}}{{/headerParams}} - - return suspendCoroutine { continuation -> - val responseListener = Response.Listener<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { response -> - continuation.resume(response) - } - - val errorListener = Response.ErrorListener { error -> - continuation.resumeWithException(error) - } - - val responseType = object : TypeToken<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}>() {}.type - - // Call the correct request builder based on whether we have a return type or a body. - // All other switching on types must be done in code inside the builder - val request: Request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> = requestFactory.build( - Request.Method.{{httpMethod}}, - "$basePath$path", - body, - headerParams, - queryParams, - formParams, - contentType, - responseType, - responseListener, - errorListener) - - postProcessors.forEach { it.invoke(request) } - - requestQueue.value.add(request) - } - } - {{/operation}} -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache deleted file mode 100644 index f7bf73ae31d0..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/api_doc.mustache +++ /dev/null @@ -1,83 +0,0 @@ -# {{classname}}{{#description}} -{{description}}{{/description}} - -All URIs are relative to *{{basePath}}* - -Method | HTTP request | Description -------------- | ------------- | ------------- -{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} -{{/operation}}{{/operations}} - -{{#operations}} -{{#operation}} - -{{summary}}{{#notes}} - -{{notes}}{{/notes}} - -### Example -```kotlin -// Import classes: -//import {{{packageName}}}.* -//import {{{packageName}}}.infrastructure.* -//import {{{modelPackage}}}.* - -val apiClient = ApiClient() -{{#authMethods}} -{{#isBasic}} -{{#isBasicBasic}} -apiClient.setCredentials("USERNAME", "PASSWORD") -{{/isBasicBasic}} -{{#isBasicBearer}} -apiClient.setBearerToken("TOKEN") -{{/isBasicBearer}} -{{/isBasic}} -{{/authMethods}} -val webService = apiClient.createWebservice({{{classname}}}::class.java) -{{#allParams}} -val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} -{{/allParams}} - -{{#useCoroutines}} -launch(Dispatchers.IO) { -{{/useCoroutines}} -{{#useCoroutines}} {{/useCoroutines}}{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}webService.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) -{{#useCoroutines}} -} -{{/useCoroutines}} -``` - -### Parameters -{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} -Name | Type | Description | Notes -------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} -{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{defaultValue}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} -{{/allParams}} - -### Return type - -{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}} - -### Authorization - -{{^authMethods}}No authorization required{{/authMethods}} -{{#authMethods}} -{{#isBasic}} -{{#isBasicBasic}} -Configure {{name}}: - ApiClient().setCredentials("USERNAME", "PASSWORD") -{{/isBasicBasic}} -{{#isBasicBearer}} -Configure {{name}}: - ApiClient().setBearerToken("TOKEN") -{{/isBasicBearer}} -{{/isBasic}} -{{/authMethods}} - -### HTTP request headers - - - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} - - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} - -{{/operation}} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache deleted file mode 100644 index 919da64ce3de..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/apikeyauth.mustache +++ /dev/null @@ -1,16 +0,0 @@ - // Api Key auth supports query header and cookie. - // Query is supported in the path generation only with a hardcoded value. - // TODO: Not sure about cookie auth form - // If implementing api key in query parameter use the ^isKeyInHeader property - - val apiKeyAuthHeaderFactoryBuilder = { - paramName: String, apiKeyPrefix: String?, apiKey: String? -> { - mapOf(paramName to - if (apiKeyPrefix != null) { - "$apiKeyPrefix $apiKey" - } else { - apiKey!! - } - ) - } - } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache deleted file mode 100644 index 5c3fb1c77c26..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/authentication.mustache +++ /dev/null @@ -1,15 +0,0 @@ -companion object Authentication { - // Where a header factory requires parameters a client will need to bind these - // TODO Generate appropriate header factories based on settings - {{#authMethods}} -{{#isApiKey}} -{{>auth/apikeyauth}} -{{/isApiKey}} -{{#isBasicBasic}} -{{>auth/httpbasicauth}} -{{/isBasicBasic}} -{{#isOAuth}} - // TODO: Oauth not implemented yet - comment out below as OAuth does not exist -{{/isOAuth}} - {{/authMethods}} - } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache deleted file mode 100644 index f4cf1e11fe6b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/httpbasicauth.mustache +++ /dev/null @@ -1,3 +0,0 @@ -val basicAuthHeaderFactoryBuilder = { username: String?, password: String? -> - { mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))} -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache deleted file mode 100644 index 81a285bc4782..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/auth/oauth.mustache +++ /dev/null @@ -1,5 +0,0 @@ -val basicAuthHeaderFactoryBuilder = { username: String?, password: String? -> -{ - throw NotImplementedError("OAuth security scheme header factory not implemented yet - see open api generator auth/oauth.mustache") - mapOf("" to "")} -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache deleted file mode 100644 index 886aecf65e30..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/bodyParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isBodyParam}}{{{paramName}}}: {{{dataType}}}{{^required}}? = null{{/required}}{{/isBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache deleted file mode 100644 index 62f0d98e6e35..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/build.mustache +++ /dev/null @@ -1,111 +0,0 @@ -{{#useAndroidMavenGradlePlugin}} -group = '{{groupId}}' -project.version = '{{artifactVersion}}' -{{/useAndroidMavenGradlePlugin}} - -buildscript { - - ext.kotlin_version = '1.5.20' - ext.swagger_annotations_version = "1.6.2" - ext.gson_version = "2.8.6" - ext.volley_version = "1.2.0" - ext.junit_version = "4.13.2" - ext.robolectric_version = "4.5.1" - ext.concurrent_unit_version = "0.4.6" - - repositories { - mavenLocal() - google() - maven { - url 'https://dl.google.com/dl/android/maven2' - } - mavenCentral() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.android.tools.build:gradle:7.4.2' - {{#useAndroidMavenGradlePlugin}} - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - {{/useAndroidMavenGradlePlugin}} - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -{{#useAndroidMavenGradlePlugin}} -apply plugin: 'com.github.dcendents.android-maven' -{{/useAndroidMavenGradlePlugin}} - -android { - compileSdkVersion 30 - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - } - compileOptions { - coreLibraryDesugaringEnabled true - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - lintOptions { - abortOnError false - } - - // Rename the aar correctly - libraryVariants.all { variant -> - variant.outputs.all { output -> - if (outputFile != null && outputFileName.endsWith('.aar')) { - outputFileName = "${archivesBaseName}-${version}.aar" - } - } - } - - testOptions { - unitTests.returnDefaultValues = true - } -} - -dependencies { - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "io.swagger:swagger-annotations:$swagger_annotations_version" - implementation "com.google.code.gson:gson:$gson_version" - implementation "com.android.volley:volley:${volley_version}" - testImplementation "junit:junit:$junit_version" - testImplementation "org.robolectric:robolectric:${robolectric_version}" - testImplementation "net.jodah:concurrentunit:${concurrent_unit_version}" - {{#generateRoomModels}} - annotationProcessor "androidx.room:room-runtime:2.3.0" - implementation "androidx.room:room-runtime:2.3.0" - {{/generateRoomModels}} -} - -afterEvaluate { - android.libraryVariants.all { variant -> - def task = project.tasks.create "jar${variant.name.capitalize()}", Jar - task.description = "Create jar artifact for ${variant.name}" - task.dependsOn variant.javaCompile - task.from variant.javaCompile.destinationDirectory - task.destinationDirectory = project.file("${project.buildDir}/outputs/jar") - task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar" - artifacts.add('archives', task); - } -} - -{{#useAndroidMavenGradlePlugin}} -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -artifacts { - archives sourcesJar -} -{{/useAndroidMavenGradlePlugin}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache deleted file mode 100644 index 98427cd4817c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/formParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isFormParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isFormParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache deleted file mode 100644 index 5b018d630a7d..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/gradle.properties.mustache +++ /dev/null @@ -1,4 +0,0 @@ -android.useAndroidX=true -{{#generateRoomModels}} -android.enableJetifier=true -{{/generateRoomModels}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache deleted file mode 100644 index 3e5c64b2cd8e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/headerParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isHeaderParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache deleted file mode 100644 index 4e8030cb56f3..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache +++ /dev/null @@ -1,56 +0,0 @@ -package {{packageName}}.infrastructure - -class CollectionFormats { - - open class CSVParams { - - var params: List - - constructor(params: List) { - this.params = params - } - - constructor(vararg params: String) { - this.params = listOf(*params) - } - - override fun toString(): String { - return params.joinToString(",") - } - } - - open class SSVParams : CSVParams { - - constructor(params: List) : super(params) - - constructor(vararg params: String) : super(*params) - - override fun toString(): String { - return params.joinToString(" ") - } - } - - class TSVParams : CSVParams { - - constructor(params: List) : super(params) - - constructor(vararg params: String) : super(*params) - - override fun toString(): String { - return params.joinToString("\t") - } - } - - class PIPESParams : CSVParams { - - constructor(params: List) : super(params) - - constructor(vararg params: String) : super(*params) - - override fun toString(): String { - return params.joinToString("|") - } - } - - class SPACEParams : SSVParams() -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache deleted file mode 100644 index 48bb25d19b56..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache +++ /dev/null @@ -1,9 +0,0 @@ -{{>licenseInfo}} -package {{packageName}}.infrastructure - -import {{roomModelPackage}}.* - -// TODO ITransformForStorage -interface ITransformForStorage { - fun toRoomModel(): T -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache deleted file mode 100644 index 1ea918bad732..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/manifest.mustache +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache deleted file mode 100644 index 4cc7de2240bd..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/pathParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isPathParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache deleted file mode 100644 index da0a0c0930ac..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/queryParams.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isQueryParam}}{{{paramName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}{{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{{dataType}}}{{/collectionFormat}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{{defaultValue}}}{{/isNumber}}{{#isNumber}}{{{dataType}}}("{{{defaultValue}}}"){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isQueryParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache deleted file mode 100644 index 9bda9b1f0b62..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/GsonRequest.mustache +++ /dev/null @@ -1,119 +0,0 @@ -package {{packageName}}.request - -import com.android.volley.NetworkResponse -import com.android.volley.ParseError -import com.android.volley.Request -import com.android.volley.Response -import com.android.volley.toolbox.HttpHeaderParser -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.google.gson.JsonSyntaxException -import java.io.UnsupportedEncodingException -import java.nio.charset.Charset -import java.net.HttpURLConnection -import java.lang.reflect.Type -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.OffsetDateTime - -import {{packageName}}.infrastructure.OffsetDateTimeAdapter -import {{packageName}}.infrastructure.LocalDateTimeAdapter -import {{packageName}}.infrastructure.LocalDateAdapter -import {{packageName}}.infrastructure.ByteArrayAdapter - -class GsonRequest( - method: Int, - url: String, - private val body: Any?, - private val headers: Map?, - private val params: MutableMap?, - private val contentTypeForBody: String?, - private val encodingForParams: String?, - private val gsonAdapters: Map?, - private val type: Type, - private val listener: Response.Listener, - errorListener: Response.ErrorListener -) : Request(method, url, errorListener) { - - val gsonBuilder: GsonBuilder = GsonBuilder() - .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter()) - .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter()) - .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter()) - .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) - .apply { - gsonAdapters?.forEach { - this.registerTypeAdapter(it.key, it.value) - } - } - - val gson: Gson by lazy { - gsonBuilder.create() - } - - private var response: NetworkResponse? = null - - override fun deliverResponse(response: T?) { - listener.onResponse(response) - } - - override fun getParams(): MutableMap? = params ?: super.getParams() - - override fun getBodyContentType(): String = contentTypeForBody ?: super.getBodyContentType() - - override fun getParamsEncoding(): String = encodingForParams ?: super.getParamsEncoding() - - override fun getHeaders(): MutableMap { - val combined = HashMap() - combined.putAll(super.getHeaders()) - if (headers != null) { - combined.putAll(headers) - } - return combined - } - - override fun getBody(): ByteArray? { - if (body != null) { - return gson.toJson(body).toByteArray(Charsets.UTF_8) - } - return super.getBody() - } - - override fun parseNetworkResponse(response: NetworkResponse?): Response { - return try { - this.response = copyTo(response) - val json = String( - response?.data ?: ByteArray(0), - Charset.forName(HttpHeaderParser.parseCharset(response?.headers)) - ) - Response.success( - gson.fromJson(json, type), - HttpHeaderParser.parseCacheHeaders(response) - ) - } catch (e: UnsupportedEncodingException) { - Response.error(ParseError(e)) - } catch (e: JsonSyntaxException) { - Response.error(ParseError(e)) - } - } - - private fun copyTo(response: NetworkResponse?): NetworkResponse { - return if (response != null) { - NetworkResponse( - response.statusCode, - response.data, - response.notModified, - response.networkTimeMs, - response.allHeaders - ) - } else { - // Return an empty response. - NetworkResponse( - HttpURLConnection.HTTP_BAD_METHOD, - ByteArray(0), - false, - 0, - emptyList() - ) - } - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache deleted file mode 100644 index 6a007c4194d9..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/IRequestFactory.mustache +++ /dev/null @@ -1,64 +0,0 @@ -package {{packageName}}.request - -import com.android.volley.Request -import com.android.volley.Response -import java.io.UnsupportedEncodingException -import java.lang.reflect.Type -import java.net.URLEncoder -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.* -import java.time.format.DateTimeFormatter -import java.time.OffsetDateTime -import java.time.LocalDate - - -interface IRequestFactory { - - companion object { - /** - * ISO 8601 date time format. - * @see https://en.wikipedia.org/wiki/ISO_8601 - */ - fun formatDateTime(datetime: OffsetDateTime) = DateTimeFormatter.ISO_INSTANT.format(datetime) - fun formatDate(date: LocalDate) = DateTimeFormatter.ISO_LOCAL_DATE.format(date) - - fun escapeString(str: String): String { - return try { - URLEncoder.encode(str, "UTF-8") - } catch (e: UnsupportedEncodingException) { - str - } - } - - fun parameterToString(param: Any?) = - when (param) { - null -> "" - is OffsetDateTime -> formatDateTime(param) - is Collection<*> -> { - val b = StringBuilder() - for (o in param) { - if (b.isNotEmpty()) { - b.append(",") - } - b.append(o.toString()) - } - b.toString() - } - else -> param.toString() - } - } - - - fun build( - method: Int, - url : String, - body: Any?, - headers: Map?, - queryParams: Map?, - formParams: Map?, - contentTypeForBody: String?, - type: Type, - responseListener: Response.Listener, - errorListener: Response.ErrorListener): Request -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache deleted file mode 100644 index 46507bff1b5d..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/jvm-volley/request/RequestFactory.mustache +++ /dev/null @@ -1,69 +0,0 @@ -// Knowing the details of an operation it will produce a call to a Volley Request constructor -package {{packageName}}.request - - -import com.android.volley.Request -import com.android.volley.Response -{{#hasAuthMethods}} -import android.util.Base64 -{{/hasAuthMethods}} -import {{packageName}}.request.IRequestFactory.Companion.escapeString -import java.lang.reflect.Type -import java.util.Locale -import java.util.UUID - -class RequestFactory(private val headerFactories : List<() -> Map> = listOf(), private val postProcessors :List <(Request<*>) -> Unit> = listOf(), private val gsonAdapters: Map = mapOf()): IRequestFactory { -{{#hasAuthMethods}} - - {{>auth/authentication}} -{{/hasAuthMethods}} - - /** - * {@inheritDoc} - */ - @Suppress("UNCHECKED_CAST") - override fun build( - method: Int, - url: String, - body: Any?, - headers: Map?, - queryParams: Map?, - formParams: Map?, - contentTypeForBody: String?, - type: Type, - responseListener: Response.Listener, - errorListener: Response.ErrorListener - ): Request { - val afterMarketHeaders = (headers?.toMutableMap() ?: mutableMapOf()) - // Factory built and aftermarket - // Merge the after market headers on top of the base ones in case we are overriding per call auth - val allHeaders = headerFactories.fold(afterMarketHeaders) { acc, factory -> (acc + factory.invoke()).toMutableMap() } - - // If we decide to support auth parameters in the url, then you will reference them by supplying a url string - // with known variable name references in the string. We will then apply - val updatedUrl = if (!queryParams.isNullOrEmpty()) { - queryParams.asSequence().fold("$url?") {acc, param -> - "$acc${escapeString(param.key)}=${escapeString(param.value)}&" - }.trimEnd('&') - } else { - url - } - - val request = GsonRequest( - method, - updatedUrl, - body, - allHeaders, - formParams?.toMutableMap(), - contentTypeForBody, - null, - gsonAdapters, - type, - responseListener, - errorListener) - - postProcessors.forEach{ it.invoke(request)} - - return request - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache deleted file mode 100644 index 249f8edbb632..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/api.mustache +++ /dev/null @@ -1,135 +0,0 @@ -{{>licenseInfo}} -package {{apiPackage}} - -{{#imports}}import {{import}} -{{/imports}} - -import {{packageName}}.infrastructure.* -import io.ktor.client.HttpClient -import io.ktor.client.HttpClientConfig -import io.ktor.client.request.forms.formData -import io.ktor.client.engine.HttpClientEngine -import kotlinx.serialization.json.Json -import io.ktor.http.ParametersBuilder -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* - -{{#operations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}open class {{classname}} : ApiClient { - - constructor( - baseUrl: String = ApiClient.BASE_URL, - httpClientEngine: HttpClientEngine? = null, - httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, - jsonSerializer: Json = ApiClient.JSON_DEFAULT - ) : super(baseUrl = baseUrl, httpClientEngine = httpClientEngine, httpClientConfig = httpClientConfig, jsonBlock = jsonSerializer) - - constructor( - baseUrl: String, - httpClient: HttpClient - ): super(baseUrl = baseUrl, httpClient = httpClient) - - {{#operation}} - {{#allParams}} - {{#isEnum}} - - /** - * enum for parameter {{paramName}} - */ - @Serializable - {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) { - {{^enumUnknownDefaultCase}} - {{#allowableValues}}{{#enumVars}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) - {{&name}}({{{value}}}){{^-last}},{{/-last}} - {{/enumVars}}{{/allowableValues}} - {{/enumUnknownDefaultCase}} - {{#enumUnknownDefaultCase}} - {{#allowableValues}}{{#enumVars}}{{^-last}} - @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}}) - {{&name}}({{{value}}}), - {{/-last}}{{/enumVars}}{{/allowableValues}} - {{/enumUnknownDefaultCase}} - } - - {{/isEnum}} - {{/allParams}} - /** - * {{summary}} - * {{notes}} - {{#allParams}} * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}} - {{/allParams}} * @return {{{returnType}}}{{^returnType}}void{{/returnType}} - */ - {{#returnType}} - @Suppress("UNCHECKED_CAST") - {{/returnType}} - open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{#isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}> { - - val localVariableAuthNames = listOf({{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}}) - - val localVariableBody = {{#hasBodyParam}}{{#bodyParam}}{{#isArray}}{{operationIdCamelCase}}Request({{{paramName}}}{{^isList}}.asList(){{/isList}}){{/isArray}}{{^isArray}}{{#isMap}}{{operationIdCamelCase}}Request({{{paramName}}}){{/isMap}}{{^isMap}}{{{paramName}}}{{/isMap}}{{/isArray}}{{/bodyParam}}{{/hasBodyParam}} - {{^hasBodyParam}} - {{#hasFormParams}} - {{#isMultipart}} - formData { - {{#formParams}} - {{#isArray}} - {{{paramName}}}?.onEach { - append("{{{baseName}}}[]", it) - } - {{/isArray}} - {{^isArray}} - {{{paramName}}}?.apply { append("{{{baseName}}}", {{{paramName}}}) } - {{/isArray}} - {{/formParams}} - } - {{/isMultipart}} - {{^isMultipart}} - ParametersBuilder().also { - {{#formParams}} - {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}.toString()) } - {{/formParams}} - }.build() - {{/isMultipart}} - {{/hasFormParams}} - {{^hasFormParams}} - io.ktor.client.utils.EmptyContent - {{/hasFormParams}} - {{/hasBodyParam}} - - val localVariableQuery = mutableMapOf>(){{#queryParams}} - {{{paramName}}}?.apply { localVariableQuery["{{baseName}}"] = {{#isContainer}}toMultiValue(this, "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf({{^isEnum}}"${{{paramName}}}"{{/isEnum}}{{#isEnum}}"${ {{paramName}}.value }"{{/isEnum}}){{/isContainer}} }{{/queryParams}} - val localVariableHeaders = mutableMapOf(){{#headerParams}} - {{{paramName}}}?.apply { localVariableHeaders["{{baseName}}"] = {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}} }{{/headerParams}} - - val localVariableConfig = RequestConfig( - RequestMethod.{{httpMethod}}, - "{{path}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}{{^isEnum}}"${{{paramName}}}"{{/isEnum}}{{#isEnum}}"${ {{paramName}}.value }"{{/isEnum}}{{/isContainer}}){{/pathParams}}, - query = localVariableQuery, - headers = localVariableHeaders, - requiresAuthentication = {{#hasAuthMethods}}true{{/hasAuthMethods}}{{^hasAuthMethods}}false{{/hasAuthMethods}}, - ) - - return {{#hasBodyParam}}jsonRequest{{/hasBodyParam}}{{^hasBodyParam}}{{#hasFormParams}}{{#isMultipart}}multipartFormRequest{{/isMultipart}}{{^isMultipart}}urlEncodedFormRequest{{/isMultipart}}{{/hasFormParams}}{{^hasFormParams}}request{{/hasFormParams}}{{/hasBodyParam}}( - localVariableConfig, - localVariableBody, - localVariableAuthNames - ).{{#isArray}}wrap<{{operationIdCamelCase}}Response>().map { value{{^isList}}.toTypedArray(){{/isList}} }{{/isArray}}{{^isArray}}{{#isMap}}wrap<{{operationIdCamelCase}}Response>().map { value }{{/isMap}}{{^isMap}}wrap(){{/isMap}}{{/isArray}} - } - -{{#hasBodyParam}} -{{#bodyParam}} -{{#isArray}}{{>serial_wrapper_request_list}}{{/isArray}}{{#isMap}}{{>serial_wrapper_request_map}}{{/isMap}} -{{/bodyParam}} -{{/hasBodyParam}} -{{#isArray}} -{{>serial_wrapper_response_list}} -{{/isArray}} -{{#isMap}} -{{>serial_wrapper_response_map}} -{{/isMap}} - - {{/operation}} -} -{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache deleted file mode 100644 index 618fd7a88903..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/ApiKeyAuth.kt.mustache +++ /dev/null @@ -1,16 +0,0 @@ -package {{packageName}}.auth - -class ApiKeyAuth(private val location: String, val paramName: String) : Authentication { - var apiKey: String? = null - var apiKeyPrefix: String? = null - - override fun apply(query: MutableMap>, headers: MutableMap) { - val key: String = apiKey ?: return - val prefix: String? = apiKeyPrefix - val value: String = if (prefix != null) "$prefix $key" else key - when (location) { - "query" -> query[paramName] = listOf(value) - "header" -> headers[paramName] = value - } - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache deleted file mode 100644 index 1aab9156d98b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/Authentication.kt.mustache +++ /dev/null @@ -1,13 +0,0 @@ -package {{packageName}}.auth - -interface Authentication { - - /** - * Apply authentication settings to header and query params. - * - * @param query Query parameters. - * @param headers Header parameters. - */ - fun apply(query: MutableMap>, headers: MutableMap) - -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache deleted file mode 100644 index 26325424e5dd..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBasicAuth.kt.mustache +++ /dev/null @@ -1,17 +0,0 @@ -package {{packageName}}.auth - -import io.ktor.util.InternalAPI -import io.ktor.util.encodeBase64 - -class HttpBasicAuth : Authentication { - var username: String? = null - var password: String? = null - - @OptIn(InternalAPI::class) - override fun apply(query: MutableMap>, headers: MutableMap) { - if (username == null && password == null) return - val str = (username ?: "") + ":" + (password ?: "") - val auth = str.encodeBase64() - headers["Authorization"] = "Basic $auth" - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache deleted file mode 100644 index 982389d09609..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/HttpBearerAuth.kt.mustache +++ /dev/null @@ -1,14 +0,0 @@ -package {{packageName}}.auth - -class HttpBearerAuth(private val scheme: String?) : Authentication { - var bearerToken: String? = null - - override fun apply(query: MutableMap>, headers: MutableMap) { - val token: String = bearerToken ?: return - headers["Authorization"] = (if (scheme != null) upperCaseBearer(scheme)!! + " " else "") + token - } - - private fun upperCaseBearer(scheme: String): String? { - return if ("bearer".equals(scheme, ignoreCase = true)) "Bearer" else scheme - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache deleted file mode 100644 index 0945969b8f55..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/auth/OAuth.kt.mustache +++ /dev/null @@ -1,10 +0,0 @@ -package {{packageName}}.auth - -class OAuth : Authentication { - var accessToken: String? = null - - override fun apply(query: MutableMap>, headers: MutableMap) { - val token: String = accessToken ?: return - headers["Authorization"] = "Bearer $token" - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache deleted file mode 100644 index e527fb53d363..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/build.gradle.kts.mustache +++ /dev/null @@ -1,103 +0,0 @@ -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget - -plugins { - kotlin("multiplatform"){{^omitGradlePluginVersions}} version "1.9.20" // kotlin_version{{/omitGradlePluginVersions}} - kotlin("plugin.serialization"){{^omitGradlePluginVersions}} version "1.9.20" // kotlin_version{{/omitGradlePluginVersions}} -} - -group = "{{groupId}}" -version = "{{artifactVersion}}" - -val kotlin_version = "1.9.20" -val coroutines_version = "1.7.3" -val serialization_version = "1.6.1" -val ktor_version = "2.3.6" - -repositories { - mavenCentral() -} - -kotlin { - jvm() - iosX64() - iosArm64() - iosSimulatorArm64() - js { - browser() - nodejs() - } - - sourceSets { - commonMain { - dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version") - - api("io.ktor:ktor-client-core:$ktor_version") - api("io.ktor:ktor-client-serialization:$ktor_version") - api("io.ktor:ktor-client-content-negotiation:$ktor_version") - api("io.ktor:ktor-serialization-kotlinx-json:$ktor_version") - - {{#kotlinx-datetime}} - api("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1") - {{/kotlinx-datetime}} - } - } - - commonTest { - dependencies { - implementation(kotlin("test")) - implementation("io.ktor:ktor-client-mock:$ktor_version") - } - } - - jvmMain { - dependencies { - implementation(kotlin("stdlib-jdk7")) - implementation("io.ktor:ktor-client-cio-jvm:$ktor_version") - } - } - - jvmTest { - dependencies { - implementation(kotlin("test-junit")) - } - } - - iosMain { - dependencies { - api("io.ktor:ktor-client-ios:$ktor_version") - } - } - - jsMain { - dependencies { - api("io.ktor:ktor-client-js:$ktor_version") - } - } - - all { - languageSettings.apply { - optIn("kotlin.Experimental") - } - } - } -} - -tasks { - register("iosTest") { - val device = project.findProperty("device")?.toString() ?: "iPhone 8" - dependsOn("linkDebugTestIosX64") - group = JavaBasePlugin.VERIFICATION_GROUP - description = "Execute unit tests on ${device} simulator" - doLast { - val binary = kotlin.targets.getByName("iosX64").binaries.getTest("DEBUG") - exec { - commandLine("xcrun", "simctl", "spawn", device, binary.outputFile) - } - } - } - register("test") { - dependsOn("allTests") - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache deleted file mode 100644 index c3bd8b18461c..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/commonTest/Coroutine.kt.mustache +++ /dev/null @@ -1,13 +0,0 @@ -{{>licenseInfo}} - -package util - -import kotlinx.coroutines.CoroutineScope - -/** -* Block the current thread until execution of the given coroutine is complete. -* -* @param block The coroutine code. -* @return The result of the coroutine. -*/ -internal expect fun runTest(block: suspend CoroutineScope.() -> T): T diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache deleted file mode 100644 index a07f17e10b01..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/ApiClient.kt.mustache +++ /dev/null @@ -1,199 +0,0 @@ -package {{packageName}}.infrastructure - -import io.ktor.client.HttpClient -import io.ktor.client.HttpClientConfig -import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.request.* -import io.ktor.client.request.forms.FormDataContent -import io.ktor.client.request.forms.MultiPartFormDataContent -import io.ktor.client.request.header -import io.ktor.client.request.parameter -import io.ktor.client.statement.HttpResponse -import io.ktor.http.ContentType -import io.ktor.serialization.kotlinx.json.json -import io.ktor.http.* -import io.ktor.http.content.PartData -import io.ktor.http.contentType -import kotlin.Unit -import kotlinx.serialization.json.Json - -import {{packageName}}.auth.* - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class ApiClient( - private val baseUrl: String -) { - - private lateinit var client: HttpClient - - constructor( - baseUrl: String, - httpClientEngine: HttpClientEngine?, - httpClientConfig: ((HttpClientConfig<*>) -> Unit)? = null, - jsonBlock: Json, - ) : this(baseUrl = baseUrl) { - val clientConfig: (HttpClientConfig<*>) -> Unit by lazy { - { - it.install(ContentNegotiation) { json(jsonBlock) } - httpClientConfig?.invoke(it) - } - } - - client = httpClientEngine?.let { HttpClient(it, clientConfig) } ?: HttpClient(clientConfig) - } - - constructor( - baseUrl: String, - httpClient: HttpClient - ): this(baseUrl = baseUrl) { - this.client = httpClient - } - - {{#hasAuthMethods}} - private val authentications: kotlin.collections.Map by lazy { - mapOf({{#authMethods}}{{#isBasic}}{{#isBasicBasic}} - "{{name}}" to HttpBasicAuth(){{/isBasicBasic}}{{#isBasicBearer}} - "{{name}}" to HttpBearerAuth("{{scheme}}"){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} - "{{name}}" to ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"){{/isApiKey}}{{#isOAuth}} - "{{name}}" to OAuth(){{/isOAuth}}{{^-last}}, {{/-last}}{{/authMethods}}) - } - {{/hasAuthMethods}} - {{^hasAuthMethods}} - private val authentications: kotlin.collections.Map? = null - {{/hasAuthMethods}} - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - const val BASE_URL = "{{{basePath}}}" - val JSON_DEFAULT = Json { - ignoreUnknownKeys = true - prettyPrint = true - isLenient = true - } - protected val UNSAFE_HEADERS = listOf(HttpHeaders.ContentType) - } - - /** - * Set the username for the first HTTP basic authentication. - * - * @param username Username - */ - fun setUsername(username: String) { - val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? - ?: throw Exception("No HTTP basic authentication configured") - auth.username = username - } - - /** - * Set the password for the first HTTP basic authentication. - * - * @param password Password - */ - fun setPassword(password: String) { - val auth = authentications?.values?.firstOrNull { it is HttpBasicAuth } as HttpBasicAuth? - ?: throw Exception("No HTTP basic authentication configured") - auth.password = password - } - - /** - * Set the API key value for the first API key authentication. - * - * @param apiKey API key - * @param paramName The name of the API key parameter, or null or set the first key. - */ - fun setApiKey(apiKey: String, paramName: String? = null) { - val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName)} as ApiKeyAuth? - ?: throw Exception("No API key authentication configured") - auth.apiKey = apiKey - } - - /** - * Set the API key prefix for the first API key authentication. - * - * @param apiKeyPrefix API key prefix - * @param paramName The name of the API key parameter, or null or set the first key. - */ - fun setApiKeyPrefix(apiKeyPrefix: String, paramName: String? = null) { - val auth = authentications?.values?.firstOrNull { it is ApiKeyAuth && (paramName == null || paramName == it.paramName) } as ApiKeyAuth? - ?: throw Exception("No API key authentication configured") - auth.apiKeyPrefix = apiKeyPrefix - } - - /** - * Set the access token for the first OAuth2 authentication. - * - * @param accessToken Access token - */ - fun setAccessToken(accessToken: String) { - val auth = authentications?.values?.firstOrNull { it is OAuth } as OAuth? - ?: throw Exception("No OAuth2 authentication configured") - auth.accessToken = accessToken - } - - /** - * Set the access token for the first Bearer authentication. - * - * @param bearerToken The bearer token. - */ - fun setBearerToken(bearerToken: String) { - val auth = authentications?.values?.firstOrNull { it is HttpBearerAuth } as HttpBearerAuth? - ?: throw Exception("No Bearer authentication configured") - auth.bearerToken = bearerToken - } - - protected suspend fun multipartFormRequest(requestConfig: RequestConfig, body: kotlin.collections.List?, authNames: kotlin.collections.List): HttpResponse { - return request(requestConfig, MultiPartFormDataContent(body ?: listOf()), authNames) - } - - protected suspend fun urlEncodedFormRequest(requestConfig: RequestConfig, body: Parameters?, authNames: kotlin.collections.List): HttpResponse { - return request(requestConfig, FormDataContent(body ?: Parameters.Empty), authNames) - } - - protected suspend fun jsonRequest(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse = request(requestConfig, body, authNames) - - protected suspend fun request(requestConfig: RequestConfig, body: Any? = null, authNames: kotlin.collections.List): HttpResponse { - requestConfig.updateForAuth(authNames) - val headers = requestConfig.headers - - return client.request { - this.url { - this.takeFrom(URLBuilder(baseUrl)) - appendPath(requestConfig.path.trimStart('/').split('/')) - requestConfig.query.forEach { query -> - query.value.forEach { value -> - parameter(query.key, value) - } - } - } - this.method = requestConfig.method.httpMethod - headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) } - if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) { - val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) } - ?: ContentType.Application.Json) - this.contentType(contentType) - this.setBody(body) - } - } - } - - private fun RequestConfig.updateForAuth(authNames: kotlin.collections.List) { - for (authName in authNames) { - val auth = authentications?.get(authName) ?: throw Exception("Authentication undefined: $authName") - auth.apply(query, headers) - } - } - - private fun URLBuilder.appendPath(components: kotlin.collections.List): URLBuilder = apply { - encodedPath = encodedPath.trimEnd('/') + components.joinToString("/", prefix = "/") { it.encodeURLQueryComponent() } - } - - private val RequestMethod.httpMethod: HttpMethod - get() = when (this) { - RequestMethod.DELETE -> HttpMethod.Delete - RequestMethod.GET -> HttpMethod.Get - RequestMethod.HEAD -> HttpMethod.Head - RequestMethod.PATCH -> HttpMethod.Patch - RequestMethod.PUT -> HttpMethod.Put - RequestMethod.POST -> HttpMethod.Post - RequestMethod.OPTIONS -> HttpMethod.Options - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache deleted file mode 100644 index b8bb160990ae..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Base64ByteArray.kt.mustache +++ /dev/null @@ -1,29 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* - -@Serializable(Base64ByteArray.Companion::class) -class Base64ByteArray(val value: ByteArray) { - companion object : KSerializer { - override val descriptor = PrimitiveSerialDescriptor("Base64ByteArray", PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, obj: Base64ByteArray) = encoder.encodeString(obj.value.encodeBase64()) - override fun deserialize(decoder: Decoder) = Base64ByteArray(decoder.decodeString().decodeBase64Bytes()) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - other as Base64ByteArray - return value.contentEquals(other.value) - } - - override fun hashCode(): Int { - return value.contentHashCode() - } - - override fun toString(): String { - return "Base64ByteArray(${hex(value)})" - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache deleted file mode 100644 index a11e0525d910..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/Bytes.kt.mustache +++ /dev/null @@ -1,101 +0,0 @@ -package {{packageName}}.infrastructure - -import io.ktor.utils.io.core.* -import kotlin.experimental.and - -private val digits = "0123456789abcdef".toCharArray() -private const val BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -private const val BASE64_MASK: Byte = 0x3f -private const val BASE64_PAD = '=' -private val BASE64_INVERSE_ALPHABET = IntArray(256) { BASE64_ALPHABET.indexOf(it.toChar()) } - -private fun String.toCharArray(): CharArray = CharArray(length) { get(it) } -private fun ByteArray.clearFrom(from: Int) = (from until size).forEach { this[it] = 0 } -private fun Int.toBase64(): Char = BASE64_ALPHABET[this] -private fun Byte.fromBase64(): Byte = BASE64_INVERSE_ALPHABET[toInt() and 0xff].toByte() and BASE64_MASK -internal fun ByteArray.encodeBase64(): String = buildPacket { writeFully(this@encodeBase64) }.encodeBase64() -internal fun String.decodeBase64Bytes(): ByteArray = buildPacket { writeText(dropLastWhile { it == BASE64_PAD }) }.decodeBase64Bytes().readBytes() - -/** - * Encode [bytes] as a HEX string with no spaces, newlines and `0x` prefixes. - * - * Taken from https://github.com/ktorio/ktor/blob/master/ktor-utils/common/src/io/ktor/util/Crypto.kt - */ -internal fun hex(bytes: ByteArray): String { - val result = CharArray(bytes.size * 2) - var resultIndex = 0 - val digits = digits - - for (element in bytes) { - val b = element.toInt() and 0xff - result[resultIndex++] = digits[b shr 4] - result[resultIndex++] = digits[b and 0x0f] - } - - return result.concatToString() -} - -/** - * Decode bytes from HEX string. It should be no spaces and `0x` prefixes. - * - * Taken from https://github.com/ktorio/ktor/blob/master/ktor-utils/common/src/io/ktor/util/Crypto.kt - */ -internal fun hex(s: String): ByteArray { - val result = ByteArray(s.length / 2) - for (idx in result.indices) { - val srcIdx = idx * 2 - val high = s[srcIdx].toString().toInt(16) shl 4 - val low = s[srcIdx + 1].toString().toInt(16) - result[idx] = (high or low).toByte() - } - - return result -} - -/** - * Encode [ByteReadPacket] in base64 format. - * - * Taken from https://github.com/ktorio/ktor/blob/424d1d2cfaa3281302c60af9500f738c8c2fc846/ktor-utils/common/src/io/ktor/util/Base64.kt - */ -private fun ByteReadPacket.encodeBase64(): String = buildString { - val data = ByteArray(3) - while (remaining > 0) { - val read = readAvailable(data) - data.clearFrom(read) - - val padSize = (data.size - read) * 8 / 6 - val chunk = ((data[0].toInt() and 0xFF) shl 16) or - ((data[1].toInt() and 0xFF) shl 8) or - (data[2].toInt() and 0xFF) - - for (index in data.size downTo padSize) { - val char = (chunk shr (6 * index)) and BASE64_MASK.toInt() - append(char.toBase64()) - } - - repeat(padSize) { append(BASE64_PAD) } - } -} - -/** - * Decode [ByteReadPacket] from base64 format - * - * Taken from https://github.com/ktorio/ktor/blob/424d1d2cfaa3281302c60af9500f738c8c2fc846/ktor-utils/common/src/io/ktor/util/Base64.kt - */ -@Suppress("DEPRECATION") -private fun ByteReadPacket.decodeBase64Bytes(): Input = buildPacket { - val data = ByteArray(4) - - while (remaining > 0) { - val read = readAvailable(data) - - val chunk = data.foldIndexed(0) { index, result, current -> - result or (current.fromBase64().toInt() shl ((3 - index) * 6)) - } - - for (index in data.size - 2 downTo (data.size - read)) { - val origin = (chunk shr (8 * index)) and 0xff - writeByte(origin.toByte()) - } - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache deleted file mode 100644 index 87a68f3084e5..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/HttpResponse.kt.mustache +++ /dev/null @@ -1,51 +0,0 @@ -package {{packageName}}.infrastructure - -import io.ktor.http.Headers -import io.ktor.http.isSuccess -import io.ktor.util.reflect.TypeInfo -import io.ktor.util.reflect.typeInfo - -{{#nonPublicApi}}internal {{/nonPublicApi}}open class HttpResponse(val response: io.ktor.client.statement.HttpResponse, val provider: BodyProvider) { - val status: Int = response.status.value - val success: Boolean = response.status.isSuccess() - val headers: Map> = response.headers.mapEntries() - suspend fun body(): T = provider.body(response) - suspend fun typedBody(type: TypeInfo): V = provider.typedBody(response, type) - - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object { - private fun Headers.mapEntries(): Map> { - val result = mutableMapOf>() - entries().forEach { result[it.key] = it.value } - return result - } - } -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}interface BodyProvider { - suspend fun body(response: io.ktor.client.statement.HttpResponse): T - suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class TypedBodyProvider(private val type: TypeInfo) : BodyProvider { - @Suppress("UNCHECKED_CAST") - override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = - response.call.body(type) as T - - @Suppress("UNCHECKED_CAST") - override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = - response.call.body(type) as V -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}class MappedBodyProvider(private val provider: BodyProvider, private val block: S.() -> T) : BodyProvider { - override suspend fun body(response: io.ktor.client.statement.HttpResponse): T = - block(provider.body(response)) - - override suspend fun typedBody(response: io.ktor.client.statement.HttpResponse, type: TypeInfo): V = - provider.typedBody(response, type) -} - -{{#nonPublicApi}}internal {{/nonPublicApi}}inline fun io.ktor.client.statement.HttpResponse.wrap(): HttpResponse = - HttpResponse(this, TypedBodyProvider(typeInfo())) - -{{#nonPublicApi}}internal {{/nonPublicApi}}fun HttpResponse.map(block: T.() -> V): HttpResponse = - HttpResponse(response, MappedBodyProvider(provider, block)) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache deleted file mode 100644 index 80c85b51c723..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/infrastructure/OctetByteArray.kt.mustache +++ /dev/null @@ -1,29 +0,0 @@ -package {{packageName}}.infrastructure - -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* - -@Serializable(OctetByteArray.Companion::class) -class OctetByteArray(val value: ByteArray) { - companion object : KSerializer { - override val descriptor = PrimitiveSerialDescriptor("OctetByteArray", PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, obj: OctetByteArray) = encoder.encodeString(hex(obj.value)) - override fun deserialize(decoder: Decoder) = OctetByteArray(hex(decoder.decodeString())) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - other as OctetByteArray - return value.contentEquals(other.value) - } - - override fun hashCode(): Int { - return value.contentHashCode() - } - - override fun toString(): String { - return "OctetByteArray(${hex(value)})" - } -} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache deleted file mode 100644 index 351c0120b7b1..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/iosTest/Coroutine.kt.mustache +++ /dev/null @@ -1,8 +0,0 @@ -{{>licenseInfo}} - -package util - -import kotlinx.coroutines.CoroutineScope -import kotlin.coroutines.EmptyCoroutineContext - -internal actual fun runTest(block: suspend CoroutineScope.() -> T): T = kotlinx.coroutines.runBlocking(EmptyCoroutineContext, block) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache deleted file mode 100644 index 23f710816de7..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jsTest/Coroutine.kt.mustache +++ /dev/null @@ -1,7 +0,0 @@ -package util - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.promise - -actual fun runTest(block: suspend (scope : CoroutineScope) -> T): dynamic = GlobalScope.promise { block(this) } diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache deleted file mode 100644 index 351c0120b7b1..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/jvmTest/Coroutine.kt.mustache +++ /dev/null @@ -1,8 +0,0 @@ -{{>licenseInfo}} - -package util - -import kotlinx.coroutines.CoroutineScope -import kotlin.coroutines.EmptyCoroutineContext - -internal actual fun runTest(block: suspend CoroutineScope.() -> T): T = kotlinx.coroutines.runBlocking(EmptyCoroutineContext, block) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache deleted file mode 100644 index 179205c3fece..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/pom.mustache +++ /dev/null @@ -1,64 +0,0 @@ - - 4.0.0 - {{groupId}} - {{artifactId}} - {{artifactVersion}} - {{appName}} - pom - - UTF-8 - - - - - maven-dependency-plugin - - - package - - copy-dependencies - - - ${project.build.directory} - - - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 - - - bundle-clean - clean - - exec - - - /bin/bash - - gradlew - clean - - - - - bundle-test - integration-test - - exec - - - /bin/bash - - gradlew - build - - - - - - - - diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache deleted file mode 100644 index 358dfa70be50..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_list.mustache +++ /dev/null @@ -1,9 +0,0 @@ - @Serializable({{operationIdCamelCase}}Request.Companion::class) - private class {{operationIdCamelCase}}Request(val value: List<{{#bodyParam}}{{baseType}}{{/bodyParam}}>) { - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Request> { - private val serializer: KSerializer> = serializer>() - override val descriptor = serializer.descriptor - override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Request) = serializer.serialize(encoder, obj.value) - override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Request(serializer.deserialize(decoder)) - } - } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache deleted file mode 100644 index 55efd01ad655..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_request_map.mustache +++ /dev/null @@ -1,9 +0,0 @@ - @Serializable({{operationIdCamelCase}}Request.Companion::class) - private class {{operationIdCamelCase}}Request(val value: Map) { - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Request> { - private val serializer: KSerializer> = serializer>() - override val descriptor = serializer.descriptor - override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Request) = serializer.serialize(encoder, obj.value) - override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Request(serializer.deserialize(decoder)) - } - } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache deleted file mode 100644 index 3f191db3a67b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_list.mustache +++ /dev/null @@ -1,9 +0,0 @@ - @Serializable({{operationIdCamelCase}}Response.Companion::class) - private class {{operationIdCamelCase}}Response(val value: List<{{returnBaseType}}>) { - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Response> { - private val serializer: KSerializer> = serializer>() - override val descriptor = serializer.descriptor - override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Response) = serializer.serialize(encoder, obj.value) - override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Response(serializer.deserialize(decoder)) - } - } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache deleted file mode 100644 index bbd342cfa1a8..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/serial_wrapper_response_map.mustache +++ /dev/null @@ -1,9 +0,0 @@ - @Serializable({{operationIdCamelCase}}Response.Companion::class) - private class {{operationIdCamelCase}}Response(val value: Map) { - {{#nonPublicApi}}internal {{/nonPublicApi}}companion object : KSerializer<{{operationIdCamelCase}}Response> { - private val serializer: KSerializer> = serializer>() - override val descriptor = serializer.descriptor - override fun serialize(encoder: Encoder, obj: {{operationIdCamelCase}}Response) = serializer.serialize(encoder, obj.value) - override fun deserialize(decoder: Decoder) = {{operationIdCamelCase}}Response(serializer.deserialize(decoder)) - } - } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache deleted file mode 100644 index ba0114cb1764..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/libraries/multiplatform/settings.gradle.kts.mustache +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "{{artifactId}}" diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache deleted file mode 100644 index d11e8934fe76..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/licenseInfo.mustache +++ /dev/null @@ -1,14 +0,0 @@ -/** - * - * Please note: - * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * Do not edit this file manually. - * - */ - -@file:Suppress( - "ArrayInDataClass", - "EnumEntryName", - "RemoveRedundantQualifierName", - "UnusedImport" -) diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache deleted file mode 100644 index 831a0e79b934..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/model.mustache +++ /dev/null @@ -1,16 +0,0 @@ -{{>licenseInfo}} -package {{modelPackage}} - -{{#imports}}import {{import}} -{{/imports}} - -{{#models}} -{{#model}} -{{^generateOneOfAnyOfWrappers}} -{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{#isAlias}}typealias {{classname}} = {{{dataType}}}{{/isAlias}}{{^isAlias}}{{>data_class}}{{/isAlias}}{{/isEnum}} -{{/generateOneOfAnyOfWrappers}} -{{#generateOneOfAnyOfWrappers}} -{{#isEnum}}{{>enum_class}}{{/isEnum}}{{^isEnum}}{{#isAlias}}typealias {{classname}} = {{{dataType}}}{{/isAlias}}{{^isAlias}}{{#oneOf}}{{#-first}}{{>oneof_class}}{{/-first}}{{/oneOf}}{{#anyOf}}{{#-first}}{{>anyof_class}}{{/-first}}{{/anyOf}}{{^oneOf}}{{^anyOf}}{{>data_class}}{{/anyOf}}{{/oneOf}}{{/isAlias}}{{/isEnum}} -{{/generateOneOfAnyOfWrappers}} -{{/model}} -{{/models}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache deleted file mode 100644 index 4c7f39007171..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/modelMutable.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#modelMutable}}var{{/modelMutable}}{{^modelMutable}}val{{/modelMutable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache deleted file mode 100644 index e3b718421188..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_doc.mustache +++ /dev/null @@ -1,3 +0,0 @@ -{{#models}}{{#model}} -{{#isEnum}}{{>enum_doc}}{{/isEnum}}{{^isEnum}}{{>class_doc}}{{/isEnum}} -{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache deleted file mode 100644 index bc442d04ea6b..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room.mustache +++ /dev/null @@ -1,38 +0,0 @@ -{{>licenseInfo}} -package {{roomModelPackage}} - -import androidx.room.Entity -import androidx.room.Ignore -import androidx.room.PrimaryKey -import {{modelPackage}}.* - -{{#models}} -{{#model}} - -@Entity(tableName = "{{classname}}") -/** -* Room model for {{{description}}} -{{#allVars}} -* @param {{{name}}} {{{description}}} -{{/allVars}} -*/ -data class {{classname}}RoomModel ( - @PrimaryKey(autoGenerate = true) var roomTableId: Int, - {{#allVars}}{{#items.isPrimitiveType}}var {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{>model_room_init_var}}{{/isArray}}, - {{/items.isPrimitiveType}}{{/allVars}} - {{#allVars}}{{^isEnum}}{{^isArray}}var {{{name}}}: {{{dataType}}}{{>model_room_init_var}}, - {{/isArray}}{{/isEnum}}{{/allVars}} - {{#allVars}}{{#isEnum}}{{^isArray}}var {{{name}}}: {{classname}}.{{{nameInPascalCase}}}{{>model_room_init_var}}, - {{/isArray}}{{/isEnum}}{{/allVars}}) { -{{#allVars}}{{#isArray}}{{#isList}}{{^items.isPrimitiveType}} - @Ignore - {{^isNullable}}{{#required}}lateinit {{/required}}{{/isNullable}}var {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInPascalCase}}}{{/items.isEnum}}>{{>model_room_init_var}}{{/isArray}} -{{/items.isPrimitiveType}}{{/isList}}{{/isArray}}{{/allVars}} - {{^discriminator}}companion object { } - - fun toApiModel(): {{classname}} = {{classname}}( - {{#allVars}}{{name}} = this.{{name}}, - {{/allVars}}){{/discriminator}} -} -{{/model}} -{{/models}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache deleted file mode 100644 index 3e6f299d3d6e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_room_init_var.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isNullable}}?{{/isNullable}}{{^required}}{{^isNullable}}?{{/isNullable}}{{/required}}{{#defaultvalue}} = {{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}{{#isNullable}} = null{{/isNullable}}{{^required}}{{^isNullable}} = null{{/isNullable}}{{/required}}{{/defaultvalue}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache deleted file mode 100644 index c13e2a054a43..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/model_test.mustache +++ /dev/null @@ -1,29 +0,0 @@ -{{>licenseInfo}} -package {{modelPackage}} - -import io.kotlintest.shouldBe -import io.kotlintest.specs.ShouldSpec - -import {{modelPackage}}.{{{classname}}} -{{#imports}}import {{import}} -{{/imports}} - -{{#models}} -{{#model}} -class {{{classname}}}Test : ShouldSpec() { - init { - // uncomment below to create an instance of {{{classname}}} - //val modelInstance = {{{classname}}}() - - {{#vars}} - // to test the property `{{{name}}}`{{#description}} - {{{.}}}{{/description}} - should("test {{{name}}}") { - // uncomment below to test the property - //modelInstance.{{{name}}} shouldBe ("TODO") - } - - {{/vars}} - } -} -{{/model}} -{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache deleted file mode 100644 index 3c1afa2af7c5..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/oneof_class.mustache +++ /dev/null @@ -1,252 +0,0 @@ -{{^multiplatform}} -{{#gson}} -{{#generateOneOfAnyOfWrappers}} -import com.google.gson.Gson -import com.google.gson.JsonElement -import com.google.gson.TypeAdapter -import com.google.gson.TypeAdapterFactory -import com.google.gson.reflect.TypeToken -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter -import com.google.gson.annotations.JsonAdapter -{{/generateOneOfAnyOfWrappers}} -import com.google.gson.annotations.SerializedName -{{/gson}} -{{#moshi}} -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -{{/moshi}} -{{#jackson}} -{{#enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonEnumDefaultValue -{{/enumUnknownDefaultCase}} -import com.fasterxml.jackson.annotation.JsonProperty -{{#discriminator}} -import com.fasterxml.jackson.annotation.JsonSubTypes -import com.fasterxml.jackson.annotation.JsonTypeInfo -{{/discriminator}} -{{/jackson}} -{{#kotlinx_serialization}} -import {{#serializableModel}}kotlinx.serialization.Serializable as KSerializable{{/serializableModel}}{{^serializableModel}}kotlinx.serialization.Serializable{{/serializableModel}} -import kotlinx.serialization.SerialName -import kotlinx.serialization.Contextual -import kotlinx.serialization.Polymorphic -{{#enumUnknownDefaultCase}} -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -{{/enumUnknownDefaultCase}} -{{#hasEnums}} -{{/hasEnums}} -{{/kotlinx_serialization}} -{{#parcelizeModels}} -import android.os.Parcelable -import kotlinx.parcelize.Parcelize -{{/parcelizeModels}} -{{/multiplatform}} -{{#multiplatform}} -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* -{{/multiplatform}} -{{#serializableModel}} -import java.io.Serializable -{{/serializableModel}} -{{#generateRoomModels}} -import {{roomModelPackage}}.{{classname}}RoomModel -import {{packageName}}.infrastructure.ITransformForStorage -{{/generateRoomModels}} -import java.io.IOException - -/** - * {{{description}}} - * - */ -{{#parcelizeModels}} -@Parcelize -{{/parcelizeModels}} -{{#multiplatform}}{{^discriminator}}@Serializable{{/discriminator}}{{/multiplatform}}{{#kotlinx_serialization}}{{#serializableModel}}@KSerializable{{/serializableModel}}{{^serializableModel}}@Serializable{{/serializableModel}}{{/kotlinx_serialization}}{{#moshi}}{{#moshiCodeGen}}@JsonClass(generateAdapter = true){{/moshiCodeGen}}{{/moshi}}{{#jackson}}{{#discriminator}}{{>typeInfoAnnotation}}{{/discriminator}}{{/jackson}} -{{#isDeprecated}} -@Deprecated(message = "This schema is deprecated.") -{{/isDeprecated}} -{{>additionalModelTypeAnnotations}} -{{#nonPublicApi}}internal {{/nonPublicApi}}data class {{classname}}( -{{^kotlinx_serialization}} - var actualInstance: Any? = null) -{{/kotlinx_serialization}} -{{#kotlinx_serialization}} - val resource: {{classname}}Resource? = null -) { -} -@Serializable -@Polymorphic -sealed interface {{classname}}Resource -{{/kotlinx_serialization}} -{{^kotlinx_serialization}} - class CustomTypeAdapterFactory : TypeAdapterFactory { - override fun create(gson: Gson, type: TypeToken): TypeAdapter? { - if (!{{classname}}::class.java.isAssignableFrom(type.rawType)) { - return null // this class only serializes '{{classname}}' and its subtypes - } - val elementAdapter = gson.getAdapter(JsonElement::class.java) - {{#composedSchemas}} - {{#oneOf}} - {{^isArray}} - {{^vendorExtensions.x-duplicated-data-type}} - val adapter{{{dataType}}} = gson.getDelegateAdapter(this, TypeToken.get({{{dataType}}}::class.java)) - {{/vendorExtensions.x-duplicated-data-type}} - {{/isArray}} - {{#isArray}} - @Suppress("UNCHECKED_CAST") - val adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} = gson.getDelegateAdapter(this, TypeToken.get(object : TypeToken<{{{dataType}}}>() {}.type)) as TypeAdapter<{{{dataType}}}> - {{/isArray}} - {{/oneOf}} - {{/composedSchemas}} - - @Suppress("UNCHECKED_CAST") - return object : TypeAdapter<{{classname}}?>() { - @Throws(IOException::class) - override fun write(out: JsonWriter,value: {{classname}}?) { - if (value?.actualInstance == null) { - elementAdapter.write(out, null) - return - } - - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - // check if the actual instance is of the type `{{{dataType}}}` - if (value.actualInstance is {{#isArray}}List<*>{{/isArray}}{{^isArray}}{{{dataType}}}{{/isArray}}) { - {{#isArray}} - val list = value.actualInstance as List - if (list.get(0) is {{{items.dataType}}}) { - val array = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonArray() - elementAdapter.write(out, array) - return - } - {{/isArray}} - {{^isArray}} - {{#isPrimitiveType}} - val primitive = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}}.toJsonTree(value.actualInstance as {{{dataType}}}?).getAsJsonPrimitive() - elementAdapter.write(out, primitive) - return - {{/isPrimitiveType}} - {{^isPrimitiveType}} - val element = adapter{{{dataType}}}.toJsonTree(value.actualInstance as {{{dataType}}}?) - elementAdapter.write(out, element) - return - {{/isPrimitiveType}} - {{/isArray}} - } - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - throw IOException("Failed to serialize as the type doesn't match oneOf schemas: {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}") - } - - @Throws(IOException::class) - override fun read(jsonReader: JsonReader): {{classname}} { - val jsonElement = elementAdapter.read(jsonReader) - var match = 0 - val errorMessages = ArrayList() - var actualAdapter: TypeAdapter<*> = elementAdapter - - {{#composedSchemas}} - {{#oneOf}} - {{^vendorExtensions.x-duplicated-data-type}} - {{^hasVars}} - // deserialize {{{dataType}}} - try { - // validate the JSON object to see if any exception is thrown - {{^isArray}} - {{#isNumber}} - require(jsonElement.getAsJsonPrimitive().isNumber()) { - String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) - } - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - require(jsonElement.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}()) { - String.format("Expected json element to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) - } - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{{dataType}}}.validateJsonElement(jsonElement) - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - {{/isPrimitiveType}} - {{/isNumber}} - {{/isArray}} - {{#isArray}} - require(jsonElement.isJsonArray) { - String.format("Expected json element to be a array type in the JSON string but got `%s`", jsonElement.toString()) - } - - // validate array items - for(element in jsonElement.getAsJsonArray()) { - {{#items}} - {{#isNumber}} - require(jsonElement.getAsJsonPrimitive().isNumber) { - String.format("Expected json element to be of type Number in the JSON string but got `%s`", jsonElement.toString()) - } - {{/isNumber}} - {{^isNumber}} - {{#isPrimitiveType}} - require(element.getAsJsonPrimitive().is{{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}}) { - String.format("Expected array items to be of type {{#isBoolean}}Boolean{{/isBoolean}}{{#isString}}String{{/isString}}{{^isString}}{{^isBoolean}}Number{{/isBoolean}}{{/isString}} in the JSON string but got `%s`", jsonElement.toString()) - } - {{/isPrimitiveType}} - {{/isNumber}} - {{^isNumber}} - {{^isPrimitiveType}} - {{{dataType}}}.validateJsonElement(element) - {{/isPrimitiveType}} - {{/isNumber}} - {{/items}} - } - actualAdapter = adapter{{#sanitizeGeneric}}{{{dataType}}}{{/sanitizeGeneric}} - {{/isArray}} - match++ - //log.log(Level.FINER, "Input data matches schema '{{{dataType}}}'") - } catch (e: Exception) { - // deserialization failed, continue - errorMessages.add(String.format("Deserialization for {{{dataType}}} failed with `%s`.", e.message)) - //log.log(Level.FINER, "Input data does not match schema '{{{dataType}}}'", e) - } - {{/hasVars}} - {{#hasVars}} - // deserialize {{{.}}} - try { - // validate the JSON object to see if any exception is thrown - {{.}}.validateJsonElement(jsonElement) - actualAdapter = adapter{{.}} - match++ - //log.log(Level.FINER, "Input data matches schema '{{{.}}}'") - } catch (e: Exception) { - // deserialization failed, continue - errorMessages.add(String.format("Deserialization for {{{.}}} failed with `%s`.", e.message)) - //log.log(Level.FINER, "Input data does not match schema '{{{.}}}'", e) - } - {{/hasVars}} - {{/vendorExtensions.x-duplicated-data-type}} - {{/oneOf}} - {{/composedSchemas}} - - if (match == 1) { - val ret = {{classname}}() - ret.actualInstance = actualAdapter.fromJsonTree(jsonElement) - return ret - } - - throw IOException(String.format("Failed deserialization for {{classname}}: %d classes match result, expected 1. Detailed failure message for oneOf schemas: %s. JSON: %s", match, errorMessages, jsonElement.toString())) - } - }.nullSafe() as TypeAdapter - } - } -} -{{/kotlinx_serialization}} diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache deleted file mode 100644 index 1639fdc5374e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/param_default_value.mustache +++ /dev/null @@ -1 +0,0 @@ -{{^isNumber}}{{#isEnum}}{{#enumDefaultValue}}{{enumName}}{{operationIdCamelCase}}.{{.}}{{/enumDefaultValue}}{{^enumDefaultValue}}null{{/enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache deleted file mode 100644 index cc08101c2a29..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/settings.gradle.mustache +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = '{{artifactId}}' diff --git a/modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache b/modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache deleted file mode 100644 index 6b3cb83b0f8e..000000000000 --- a/modules/openapi-generator/src/main/resources/tidal-kotlin/typeInfoAnnotation.mustache +++ /dev/null @@ -1,6 +0,0 @@ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "{{{discriminator.propertyBaseName}}}", visible = true) -@JsonSubTypes( -{{#discriminator.mappedModels}} - JsonSubTypes.Type(value = {{modelName}}::class, name = "{{^vendorExtensions.x-discriminator-value}}{{mappingName}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}"){{^-last}},{{/-last}} -{{/discriminator.mappedModels}} -) \ No newline at end of file From 58ba6b2b8afb82bdfa1352e6c341fa2edda1554f Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Tue, 29 Oct 2024 15:20:20 +0100 Subject: [PATCH 12/13] Add generation of Utils file --- .../openapitools/codegen/languages/KotlinClientCodegen.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java index 1aeacd0b143d..0f130e478800 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java @@ -897,6 +897,9 @@ private void commonSupportingFiles() { supportingFiles.add(new SupportingFile("gradle-wrapper.properties.mustache", "gradle.wrapper".replace(".", File.separator), "gradle-wrapper.properties")); supportingFiles.add(new SupportingFile("gradle-wrapper.jar", "gradle.wrapper".replace(".", File.separator), "gradle-wrapper.jar")); } + if (getSerializationLibrary().equals(SERIALIZATION_LIBRARY_TYPE.kotlinx_serialization) && getGenerateOneOfAnyOfWrappers()) { + supportingFiles.add(new SupportingFile("Utils.kt.mustache", (sourceFolder + "." + modelPackage).replace(".", File.separator), "Utils.kt")); + } } @Override From 878be93c92b547393d6e70a393ae7dde4ce90363 Mon Sep 17 00:00:00 2001 From: Michael Pohl Date: Thu, 31 Oct 2024 05:58:11 +0100 Subject: [PATCH 13/13] Adjust generator workflow to our fork needs --- .github/workflows/openapi-generator.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/openapi-generator.yaml b/.github/workflows/openapi-generator.yaml index a91c44b58699..c4678365f435 100644 --- a/.github/workflows/openapi-generator.yaml +++ b/.github/workflows/openapi-generator.yaml @@ -39,11 +39,20 @@ jobs: ${{ runner.os }}-build-${{ env.cache-name }}- ${{ runner.os }}-build- - name: Run maven - run: ./mvnw clean --no-snapshot-updates --batch-mode --quiet install -DskipTests -Dorg.slf4j.simpleLogger.defaultLogLevel=error + run: ./mvnw clean package -DskipTests env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - run: ls -la modules/openapi-generator-cli/target + - name: Extract current branch name + id: branch-name + run: | + BRANCH_NAME=${{ github.ref }} + echo "Full ref: $BRANCH_NAME" + # Strip the refs/heads/ part to get the branch name + BRANCH_NAME=${BRANCH_NAME#refs/heads/} + echo "::set-output name=branch_name::$BRANCH_NAME" + - name: Generate tag string id: generate_snapshot_string run: | @@ -70,7 +79,7 @@ jobs: asset_name: openapi-generator-cli.jar tag: ${{ steps.generate_snapshot_string.outputs.snapshot_string }} overwrite: true - target_commit: "" + target_commit: ${{ steps.branch-name.outputs.branch_name }} body: "This is the latest snapshot release of our openapi-generator fork" # test: # name: Unit tests