@goast/kotlin 0.6.0-beta.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +45 -45
  3. package/assets/client/okhttp3/ApiAbstractions.kt +30 -30
  4. package/assets/client/okhttp3/ApiClient.kt +252 -252
  5. package/assets/client/okhttp3/ApiResponse.kt +43 -43
  6. package/assets/client/okhttp3/Errors.kt +21 -21
  7. package/assets/client/okhttp3/PartConfig.kt +11 -11
  8. package/assets/client/okhttp3/RequestConfig.kt +18 -18
  9. package/assets/client/okhttp3/RequestMethod.kt +8 -8
  10. package/assets/client/okhttp3/ResponseExtensions.kt +24 -24
  11. package/assets/client/spring-reactive-web-clients/ApiRequestFile.kt +33 -33
  12. package/esm/src/ast/references/jackson.d.ts +8 -0
  13. package/esm/src/ast/references/jackson.d.ts.map +1 -1
  14. package/esm/src/ast/references/jackson.js +8 -0
  15. package/esm/src/generators/file-generator.d.ts +21 -1
  16. package/esm/src/generators/file-generator.d.ts.map +1 -1
  17. package/esm/src/generators/file-generator.js +41 -2
  18. package/esm/src/generators/models/args.d.ts +5 -0
  19. package/esm/src/generators/models/args.d.ts.map +1 -1
  20. package/esm/src/generators/models/model-generator.d.ts +23 -0
  21. package/esm/src/generators/models/model-generator.d.ts.map +1 -1
  22. package/esm/src/generators/models/model-generator.js +130 -13
  23. package/esm/src/generators/services/okhttp3-clients/okhttp3-client-generator.d.ts +9 -0
  24. package/esm/src/generators/services/okhttp3-clients/okhttp3-client-generator.d.ts.map +1 -1
  25. package/esm/src/generators/services/okhttp3-clients/okhttp3-client-generator.js +59 -41
  26. package/esm/src/generators/services/okhttp3-clients/okhttp3-clients-generator.d.ts.map +1 -1
  27. package/esm/src/generators/services/okhttp3-clients/okhttp3-clients-generator.js +11 -9
  28. package/esm/src/generators/services/spring-controllers/spring-controller-generator.d.ts.map +1 -1
  29. package/esm/src/generators/services/spring-controllers/spring-controller-generator.js +53 -35
  30. package/esm/src/generators/services/spring-reactive-web-clients/args.d.ts +3 -0
  31. package/esm/src/generators/services/spring-reactive-web-clients/args.d.ts.map +1 -1
  32. package/esm/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.d.ts +7 -0
  33. package/esm/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.d.ts.map +1 -1
  34. package/esm/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.js +41 -20
  35. package/esm/src/import-collection.d.ts.map +1 -1
  36. package/esm/src/import-collection.js +2 -2
  37. package/esm/src/utils.d.ts +3 -1
  38. package/esm/src/utils.d.ts.map +1 -1
  39. package/esm/src/utils.js +7 -0
  40. package/package.json +2 -2
  41. package/script/src/ast/references/jackson.d.ts +8 -0
  42. package/script/src/ast/references/jackson.d.ts.map +1 -1
  43. package/script/src/ast/references/jackson.js +9 -1
  44. package/script/src/generators/file-generator.d.ts +21 -1
  45. package/script/src/generators/file-generator.d.ts.map +1 -1
  46. package/script/src/generators/file-generator.js +41 -2
  47. package/script/src/generators/models/args.d.ts +5 -0
  48. package/script/src/generators/models/args.d.ts.map +1 -1
  49. package/script/src/generators/models/model-generator.d.ts +23 -0
  50. package/script/src/generators/models/model-generator.d.ts.map +1 -1
  51. package/script/src/generators/models/model-generator.js +129 -12
  52. package/script/src/generators/services/okhttp3-clients/okhttp3-client-generator.d.ts +9 -0
  53. package/script/src/generators/services/okhttp3-clients/okhttp3-client-generator.d.ts.map +1 -1
  54. package/script/src/generators/services/okhttp3-clients/okhttp3-client-generator.js +58 -40
  55. package/script/src/generators/services/okhttp3-clients/okhttp3-clients-generator.d.ts.map +1 -1
  56. package/script/src/generators/services/okhttp3-clients/okhttp3-clients-generator.js +11 -9
  57. package/script/src/generators/services/spring-controllers/spring-controller-generator.d.ts.map +1 -1
  58. package/script/src/generators/services/spring-controllers/spring-controller-generator.js +52 -34
  59. package/script/src/generators/services/spring-reactive-web-clients/args.d.ts +3 -0
  60. package/script/src/generators/services/spring-reactive-web-clients/args.d.ts.map +1 -1
  61. package/script/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.d.ts +7 -0
  62. package/script/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.d.ts.map +1 -1
  63. package/script/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.js +40 -19
  64. package/script/src/import-collection.d.ts.map +1 -1
  65. package/script/src/import-collection.js +1 -1
  66. package/script/src/utils.d.ts +3 -1
  67. package/script/src/utils.d.ts.map +1 -1
  68. package/script/src/utils.js +9 -0
  69. package/src/mod.ts +8 -0
  70. package/src/src/assets.ts +9 -0
  71. package/src/src/ast/_index.ts +66 -0
  72. package/src/src/ast/common.ts +1 -0
  73. package/src/src/ast/index.ts +1 -0
  74. package/src/src/ast/node.ts +10 -0
  75. package/src/src/ast/nodes/annotation.ts +79 -0
  76. package/src/src/ast/nodes/argument.ts +62 -0
  77. package/src/src/ast/nodes/call.ts +75 -0
  78. package/src/src/ast/nodes/class.ts +178 -0
  79. package/src/src/ast/nodes/collection-literal.ts +49 -0
  80. package/src/src/ast/nodes/constructor.ts +126 -0
  81. package/src/src/ast/nodes/doc-tag.ts +138 -0
  82. package/src/src/ast/nodes/doc.ts +111 -0
  83. package/src/src/ast/nodes/enum-value.ts +100 -0
  84. package/src/src/ast/nodes/enum.ts +163 -0
  85. package/src/src/ast/nodes/function.ts +178 -0
  86. package/src/src/ast/nodes/generic-parameter.ts +54 -0
  87. package/src/src/ast/nodes/init-block.ts +38 -0
  88. package/src/src/ast/nodes/interface.ts +133 -0
  89. package/src/src/ast/nodes/lambda-type.ts +73 -0
  90. package/src/src/ast/nodes/lambda.ts +74 -0
  91. package/src/src/ast/nodes/object.ts +102 -0
  92. package/src/src/ast/nodes/parameter.ts +118 -0
  93. package/src/src/ast/nodes/property.ts +225 -0
  94. package/src/src/ast/nodes/reference.ts +178 -0
  95. package/src/src/ast/nodes/string.ts +114 -0
  96. package/src/src/ast/nodes/types.ts +23 -0
  97. package/src/src/ast/references/index.ts +10 -0
  98. package/src/src/ast/references/jackson.ts +70 -0
  99. package/src/src/ast/references/jakarta.ts +14 -0
  100. package/src/src/ast/references/java.ts +27 -0
  101. package/src/src/ast/references/kotlin.ts +41 -0
  102. package/src/src/ast/references/kotlinx.ts +14 -0
  103. package/src/src/ast/references/okhttp3.ts +5 -0
  104. package/src/src/ast/references/reactor.ts +5 -0
  105. package/src/src/ast/references/spring-reactive.ts +37 -0
  106. package/src/src/ast/references/spring.ts +86 -0
  107. package/src/src/ast/references/swagger.ts +23 -0
  108. package/src/src/ast/utils/get-kotlin-builder-options.ts +19 -0
  109. package/src/src/ast/utils/to-kt-node.ts +31 -0
  110. package/src/src/ast/utils/write-kt-annotations.ts +15 -0
  111. package/src/src/ast/utils/write-kt-arguments.ts +45 -0
  112. package/src/src/ast/utils/write-kt-enum-values.ts +27 -0
  113. package/src/src/ast/utils/write-kt-generic-parameters.ts +12 -0
  114. package/src/src/ast/utils/write-kt-members.ts +25 -0
  115. package/src/src/ast/utils/write-kt-node.ts +37 -0
  116. package/src/src/ast/utils/write-kt-parameters.ts +25 -0
  117. package/src/src/common-results.ts +4 -0
  118. package/src/src/config.ts +61 -0
  119. package/src/src/file-builder.ts +112 -0
  120. package/src/src/generators/file-generator.ts +71 -0
  121. package/src/src/generators/index.ts +5 -0
  122. package/src/src/generators/models/args.ts +142 -0
  123. package/src/src/generators/models/index.ts +4 -0
  124. package/src/src/generators/models/model-generator.ts +850 -0
  125. package/src/src/generators/models/models-generator.ts +65 -0
  126. package/src/src/generators/models/models.ts +95 -0
  127. package/src/src/generators/services/okhttp3-clients/args.ts +90 -0
  128. package/src/src/generators/services/okhttp3-clients/index.ts +4 -0
  129. package/src/src/generators/services/okhttp3-clients/models.ts +73 -0
  130. package/src/src/generators/services/okhttp3-clients/okhttp3-client-generator.ts +639 -0
  131. package/src/src/generators/services/okhttp3-clients/okhttp3-clients-generator.ts +190 -0
  132. package/src/src/generators/services/okhttp3-clients/refs.ts +59 -0
  133. package/src/src/generators/services/spring-controllers/args.ts +95 -0
  134. package/src/src/generators/services/spring-controllers/index.ts +4 -0
  135. package/src/src/generators/services/spring-controllers/models.ts +76 -0
  136. package/src/src/generators/services/spring-controllers/refs.ts +17 -0
  137. package/src/src/generators/services/spring-controllers/spring-controller-generator.ts +1129 -0
  138. package/src/src/generators/services/spring-controllers/spring-controllers-generator.ts +140 -0
  139. package/src/src/generators/services/spring-reactive-web-clients/args.ts +114 -0
  140. package/src/src/generators/services/spring-reactive-web-clients/index.ts +4 -0
  141. package/src/src/generators/services/spring-reactive-web-clients/models.ts +78 -0
  142. package/src/src/generators/services/spring-reactive-web-clients/refs.ts +11 -0
  143. package/src/src/generators/services/spring-reactive-web-clients/spring-reactive-web-client-generator.ts +657 -0
  144. package/src/src/generators/services/spring-reactive-web-clients/spring-reactive-web-clients-generator.ts +125 -0
  145. package/src/src/import-collection.ts +98 -0
  146. package/src/src/types.ts +3 -0
  147. package/src/src/utils.ts +55 -0
@@ -1,252 +1,252 @@
1
- package @PACKAGE_NAME@
2
-
3
- import com.fasterxml.jackson.core.type.TypeReference
4
- import com.fasterxml.jackson.databind.ObjectMapper
5
- import okhttp3.FormBody
6
- import okhttp3.Headers.Companion.toHeaders
7
- import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
8
- import okhttp3.MediaType.Companion.toMediaTypeOrNull
9
- import okhttp3.MultipartBody
10
- import okhttp3.Call.Factory
11
- import okhttp3.OkHttpClient
12
- import okhttp3.Request
13
- import okhttp3.RequestBody
14
- import okhttp3.RequestBody.Companion.asRequestBody
15
- import okhttp3.RequestBody.Companion.toRequestBody
16
- import okhttp3.ResponseBody
17
- import java.io.File
18
- import java.net.URLConnection
19
- import java.time.LocalDate
20
- import java.time.LocalDateTime
21
- import java.time.LocalTime
22
- import java.time.OffsetDateTime
23
- import java.time.OffsetTime
24
- import java.util.Locale
25
-
26
- open class ApiClient(@API_CLIENT_PARAMETERS@) {
27
- companion object {
28
- protected const val ContentType = "Content-Type"
29
- protected const val Accept = "Accept"
30
- protected const val Authorization = "Authorization"
31
- protected const val JsonMediaType = "application/json"
32
- protected const val FormDataMediaType = "multipart/form-data"
33
- protected const val FormUrlEncMediaType = "application/x-www-form-urlencoded"
34
- protected const val XmlMediaType = "application/xml"
35
-
36
- val apiKey: MutableMap<String, String> = mutableMapOf()
37
- val apiKeyPrefix: MutableMap<String, String> = mutableMapOf()
38
- var username: String? = null
39
- var password: String? = null
40
- var accessToken: String? = null
41
- const val baseUrlKey = "org.openapitools.client.baseUrl"
42
-
43
- @JvmStatic
44
- val defaultClient: OkHttpClient by lazy {
45
- builder.build()
46
- }
47
-
48
- @JvmStatic
49
- val builder: OkHttpClient.Builder = OkHttpClient.Builder()
50
- }
51
-
52
- /**
53
- * Guess Content-Type header from the given file (defaults to "application/octet-stream").
54
- *
55
- * @param file The given file
56
- * @return The guessed Content-Type
57
- */
58
- protected fun guessContentTypeFromFile(file: File): String {
59
- val contentType = URLConnection.guessContentTypeFromName(file.name)
60
- return contentType ?: "application/octet-stream"
61
- }
62
-
63
- protected inline fun <reified T> requestBody(content: T, mediaType: String?): RequestBody =
64
- when {
65
- content is File -> content.asRequestBody(
66
- (mediaType ?: guessContentTypeFromFile(content)).toMediaTypeOrNull()
67
- )
68
-
69
- mediaType == FormDataMediaType ->
70
- MultipartBody.Builder()
71
- .setType(MultipartBody.FORM)
72
- .apply {
73
- // content's type *must* be Map<String, PartConfig<*>>
74
- @Suppress("UNCHECKED_CAST")
75
- (content as Map<String, PartConfig<*>>).forEach { (name, part) ->
76
- if (part.body is File) {
77
- val partHeaders = part.headers.toMutableMap() +
78
- ("Content-Disposition" to "form-data; name=\"$name\"; filename=\"${part.body.name}\"")
79
- val fileMediaType = guessContentTypeFromFile(part.body).toMediaTypeOrNull()
80
- addPart(
81
- partHeaders.toHeaders(),
82
- part.body.asRequestBody(fileMediaType)
83
- )
84
- } else {
85
- val partHeaders = part.headers.toMutableMap() +
86
- ("Content-Disposition" to "form-data; name=\"$name\"")
87
- addPart(
88
- partHeaders.toHeaders(),
89
- objectMapper.writeValueAsString(part.body)
90
- .toRequestBody(JsonMediaType.toMediaTypeOrNull())
91
- )
92
- }
93
- }
94
- }.build()
95
-
96
- mediaType == FormUrlEncMediaType -> {
97
- FormBody.Builder().apply {
98
- // content's type *must* be Map<String, PartConfig<*>>
99
- @Suppress("UNCHECKED_CAST")
100
- (content as Map<String, PartConfig<*>>).forEach { (name, part) ->
101
- add(name, parameterToString(part.body))
102
- }
103
- }.build()
104
- }
105
-
106
- mediaType == null || mediaType.startsWith("application/") && mediaType.endsWith("json") ->
107
- if (content == null) {
108
- ByteArray(0).toRequestBody(null)
109
- } else {
110
- objectMapper.writeValueAsString(content)
111
- .toRequestBody((mediaType ?: JsonMediaType).toMediaTypeOrNull())
112
- }
113
-
114
- mediaType == XmlMediaType -> throw UnsupportedOperationException("xml not currently supported.")
115
- // TODO: this should be extended with other serializers
116
- else -> throw UnsupportedOperationException("requestBody currently only supports JSON body and File body.")
117
- }
118
-
119
- protected inline fun <reified T : Any?> responseBody(body: ResponseBody?, mediaType: String? = JsonMediaType): T? {
120
- if (body == null) {
121
- return null
122
- }
123
- if (T::class.java == File::class.java) {
124
- // return tempFile
125
- // 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
126
- val tempFile = java.nio.file.Files.createTempFile("tmp.org.openapitools.client", null).toFile()
127
- tempFile.deleteOnExit()
128
- body.byteStream().use { inputStream ->
129
- tempFile.outputStream().use { tempFileOutputStream ->
130
- inputStream.copyTo(tempFileOutputStream)
131
- }
132
- }
133
- return tempFile as T
134
- }
135
- val bodyContent = body.string()
136
- if (bodyContent.isEmpty()) {
137
- return null
138
- }
139
- return when {
140
- mediaType == null || (mediaType.startsWith("application/") && mediaType.endsWith("json")) ->
141
- objectMapper.readValue(bodyContent, object : TypeReference<T>() {})
142
-
143
- mediaType.startsWith("text/") && T::class.java == String::class.java -> bodyContent as T
144
-
145
- else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.")
146
- }
147
- }
148
-
149
- protected inline fun <reified I, reified T : Any?> request(requestConfig: RequestConfig<I>): ApiResponse<T?> {
150
- val httpUrl = baseUrl.toHttpUrlOrNull() ?: throw IllegalStateException("baseUrl is invalid.")
151
-
152
- val url = httpUrl.newBuilder()
153
- .addEncodedPathSegments(requestConfig.path.trimStart('/'))
154
- .apply {
155
- requestConfig.query.forEach { query ->
156
- query.value.forEach { queryValue ->
157
- addQueryParameter(query.key, queryValue)
158
- }
159
- }
160
- }.build()
161
-
162
- // take content-type/accept from spec or set to default (application/json) if not defined
163
- if (requestConfig.body != null && requestConfig.headers[ContentType].isNullOrEmpty()) {
164
- requestConfig.headers[ContentType] = JsonMediaType
165
- }
166
- if (requestConfig.headers[Accept].isNullOrEmpty()) {
167
- requestConfig.headers[Accept] = JsonMediaType
168
- }
169
- val headers = requestConfig.headers
170
-
171
- if (headers[Accept].isNullOrEmpty()) {
172
- throw kotlin.IllegalStateException("Missing Accept header. This is required.")
173
- }
174
-
175
- val contentType = if (headers[ContentType] != null) {
176
- // TODO: support multiple contentType options here.
177
- (headers[ContentType] as String).substringBefore(";").lowercase(Locale.getDefault())
178
- } else {
179
- null
180
- }
181
-
182
- val request = when (requestConfig.method) {
183
- RequestMethod.DELETE -> Request.Builder().url(url).delete(requestBody(requestConfig.body, contentType))
184
- RequestMethod.GET -> Request.Builder().url(url)
185
- RequestMethod.HEAD -> Request.Builder().url(url).head()
186
- RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(requestConfig.body, contentType))
187
- RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(requestConfig.body, contentType))
188
- RequestMethod.POST -> Request.Builder().url(url).post(requestBody(requestConfig.body, contentType))
189
- RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null)
190
- }.apply {
191
- headers.forEach { header -> addHeader(header.key, header.value) }
192
- }.build()
193
-
194
- val response = client.newCall(request).execute()
195
-
196
- val accept = response.header(ContentType)?.substringBefore(";")?.lowercase(Locale.getDefault())
197
-
198
- // TODO: handle specific mapping types. e.g. Map<int, Class<?>>
199
- return when {
200
- response.isRedirect -> Redirection(
201
- response.code,
202
- response.headers.toMultimap()
203
- )
204
-
205
- response.isInformational -> Informational(
206
- response.message,
207
- response.code,
208
- response.headers.toMultimap()
209
- )
210
-
211
- response.isSuccessful -> Success(
212
- responseBody(response.body, accept),
213
- response.code,
214
- response.headers.toMultimap()
215
- )
216
-
217
- response.isClientError -> ClientError(
218
- response.message,
219
- response.body?.string(),
220
- response.code,
221
- response.headers.toMultimap()
222
- )
223
-
224
- else -> ServerError(
225
- response.message,
226
- response.body?.string(),
227
- response.code,
228
- response.headers.toMultimap()
229
- )
230
- }
231
- }
232
-
233
- protected fun parameterToString(value: Any?): String = when (value) {
234
- null -> ""
235
- is Array<*> -> toMultiValue(value, "csv").toString()
236
- is Iterable<*> -> toMultiValue(value, "csv").toString()
237
- is OffsetDateTime, is OffsetTime, is LocalDateTime, is LocalDate, is LocalTime ->
238
- parseDateToQueryString(value)
239
-
240
- else -> value.toString()
241
- }
242
-
243
- protected fun parseDateToQueryString(value: Any): String {
244
- /*
245
- .replace("\"", "") converts the json object string to an actual string for the query parameter.
246
- The moshi or gson adapter allows a more generic solution instead of trying to use a native
247
- formatter. It also easily allows to provide a simple way to define a custom date format pattern
248
- inside a gson/moshi adapter.
249
- */
250
- return objectMapper.writeValueAsString(value).replace("\"", "")
251
- }
252
- }
1
+ package @PACKAGE_NAME@
2
+
3
+ import com.fasterxml.jackson.core.type.TypeReference
4
+ import com.fasterxml.jackson.databind.ObjectMapper
5
+ import okhttp3.FormBody
6
+ import okhttp3.Headers.Companion.toHeaders
7
+ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
8
+ import okhttp3.MediaType.Companion.toMediaTypeOrNull
9
+ import okhttp3.MultipartBody
10
+ import okhttp3.Call.Factory
11
+ import okhttp3.OkHttpClient
12
+ import okhttp3.Request
13
+ import okhttp3.RequestBody
14
+ import okhttp3.RequestBody.Companion.asRequestBody
15
+ import okhttp3.RequestBody.Companion.toRequestBody
16
+ import okhttp3.ResponseBody
17
+ import java.io.File
18
+ import java.net.URLConnection
19
+ import java.time.LocalDate
20
+ import java.time.LocalDateTime
21
+ import java.time.LocalTime
22
+ import java.time.OffsetDateTime
23
+ import java.time.OffsetTime
24
+ import java.util.Locale
25
+
26
+ open class ApiClient(@API_CLIENT_PARAMETERS@) {
27
+ companion object {
28
+ protected const val ContentType = "Content-Type"
29
+ protected const val Accept = "Accept"
30
+ protected const val Authorization = "Authorization"
31
+ protected const val JsonMediaType = "application/json"
32
+ protected const val FormDataMediaType = "multipart/form-data"
33
+ protected const val FormUrlEncMediaType = "application/x-www-form-urlencoded"
34
+ protected const val XmlMediaType = "application/xml"
35
+
36
+ val apiKey: MutableMap<String, String> = mutableMapOf()
37
+ val apiKeyPrefix: MutableMap<String, String> = mutableMapOf()
38
+ var username: String? = null
39
+ var password: String? = null
40
+ var accessToken: String? = null
41
+ const val baseUrlKey = "org.openapitools.client.baseUrl"
42
+
43
+ @JvmStatic
44
+ val defaultClient: OkHttpClient by lazy {
45
+ builder.build()
46
+ }
47
+
48
+ @JvmStatic
49
+ val builder: OkHttpClient.Builder = OkHttpClient.Builder()
50
+ }
51
+
52
+ /**
53
+ * Guess Content-Type header from the given file (defaults to "application/octet-stream").
54
+ *
55
+ * @param file The given file
56
+ * @return The guessed Content-Type
57
+ */
58
+ protected fun guessContentTypeFromFile(file: File): String {
59
+ val contentType = URLConnection.guessContentTypeFromName(file.name)
60
+ return contentType ?: "application/octet-stream"
61
+ }
62
+
63
+ protected inline fun <reified T> requestBody(content: T, mediaType: String?): RequestBody =
64
+ when {
65
+ content is File -> content.asRequestBody(
66
+ (mediaType ?: guessContentTypeFromFile(content)).toMediaTypeOrNull()
67
+ )
68
+
69
+ mediaType == FormDataMediaType ->
70
+ MultipartBody.Builder()
71
+ .setType(MultipartBody.FORM)
72
+ .apply {
73
+ // content's type *must* be Map<String, PartConfig<*>>
74
+ @Suppress("UNCHECKED_CAST")
75
+ (content as Map<String, PartConfig<*>>).forEach { (name, part) ->
76
+ if (part.body is File) {
77
+ val partHeaders = part.headers.toMutableMap() +
78
+ ("Content-Disposition" to "form-data; name=\"$name\"; filename=\"${part.body.name}\"")
79
+ val fileMediaType = guessContentTypeFromFile(part.body).toMediaTypeOrNull()
80
+ addPart(
81
+ partHeaders.toHeaders(),
82
+ part.body.asRequestBody(fileMediaType)
83
+ )
84
+ } else {
85
+ val partHeaders = part.headers.toMutableMap() +
86
+ ("Content-Disposition" to "form-data; name=\"$name\"")
87
+ addPart(
88
+ partHeaders.toHeaders(),
89
+ objectMapper.writeValueAsString(part.body)
90
+ .toRequestBody(JsonMediaType.toMediaTypeOrNull())
91
+ )
92
+ }
93
+ }
94
+ }.build()
95
+
96
+ mediaType == FormUrlEncMediaType -> {
97
+ FormBody.Builder().apply {
98
+ // content's type *must* be Map<String, PartConfig<*>>
99
+ @Suppress("UNCHECKED_CAST")
100
+ (content as Map<String, PartConfig<*>>).forEach { (name, part) ->
101
+ add(name, parameterToString(part.body))
102
+ }
103
+ }.build()
104
+ }
105
+
106
+ mediaType == null || mediaType.startsWith("application/") && mediaType.endsWith("json") ->
107
+ if (content == null) {
108
+ ByteArray(0).toRequestBody(null)
109
+ } else {
110
+ objectMapper.writeValueAsString(content)
111
+ .toRequestBody((mediaType ?: JsonMediaType).toMediaTypeOrNull())
112
+ }
113
+
114
+ mediaType == XmlMediaType -> throw UnsupportedOperationException("xml not currently supported.")
115
+ // TODO: this should be extended with other serializers
116
+ else -> throw UnsupportedOperationException("requestBody currently only supports JSON body and File body.")
117
+ }
118
+
119
+ protected inline fun <reified T : Any?> responseBody(body: ResponseBody?, mediaType: String? = JsonMediaType): T? {
120
+ if (body == null) {
121
+ return null
122
+ }
123
+ if (T::class.java == File::class.java) {
124
+ // return tempFile
125
+ // 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
126
+ val tempFile = java.nio.file.Files.createTempFile("tmp.org.openapitools.client", null).toFile()
127
+ tempFile.deleteOnExit()
128
+ body.byteStream().use { inputStream ->
129
+ tempFile.outputStream().use { tempFileOutputStream ->
130
+ inputStream.copyTo(tempFileOutputStream)
131
+ }
132
+ }
133
+ return tempFile as T
134
+ }
135
+ val bodyContent = body.string()
136
+ if (bodyContent.isEmpty()) {
137
+ return null
138
+ }
139
+ return when {
140
+ mediaType == null || (mediaType.startsWith("application/") && mediaType.endsWith("json")) ->
141
+ objectMapper.readValue(bodyContent, object : TypeReference<T>() {})
142
+
143
+ mediaType.startsWith("text/") && T::class.java == String::class.java -> bodyContent as T
144
+
145
+ else -> throw UnsupportedOperationException("responseBody currently only supports JSON body.")
146
+ }
147
+ }
148
+
149
+ protected inline fun <reified I, reified T : Any?> request(requestConfig: RequestConfig<I>): ApiResponse<T?> {
150
+ val httpUrl = baseUrl.toHttpUrlOrNull() ?: throw IllegalStateException("baseUrl is invalid.")
151
+
152
+ val url = httpUrl.newBuilder()
153
+ .addEncodedPathSegments(requestConfig.path.trimStart('/'))
154
+ .apply {
155
+ requestConfig.query.forEach { query ->
156
+ query.value.forEach { queryValue ->
157
+ addQueryParameter(query.key, queryValue)
158
+ }
159
+ }
160
+ }.build()
161
+
162
+ // take content-type/accept from spec or set to default (application/json) if not defined
163
+ if (requestConfig.body != null && requestConfig.headers[ContentType].isNullOrEmpty()) {
164
+ requestConfig.headers[ContentType] = JsonMediaType
165
+ }
166
+ if (requestConfig.headers[Accept].isNullOrEmpty()) {
167
+ requestConfig.headers[Accept] = JsonMediaType
168
+ }
169
+ val headers = requestConfig.headers
170
+
171
+ if (headers[Accept].isNullOrEmpty()) {
172
+ throw kotlin.IllegalStateException("Missing Accept header. This is required.")
173
+ }
174
+
175
+ val contentType = if (headers[ContentType] != null) {
176
+ // TODO: support multiple contentType options here.
177
+ (headers[ContentType] as String).substringBefore(";").lowercase(Locale.getDefault())
178
+ } else {
179
+ null
180
+ }
181
+
182
+ val request = when (requestConfig.method) {
183
+ RequestMethod.DELETE -> Request.Builder().url(url).delete(requestBody(requestConfig.body, contentType))
184
+ RequestMethod.GET -> Request.Builder().url(url)
185
+ RequestMethod.HEAD -> Request.Builder().url(url).head()
186
+ RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(requestConfig.body, contentType))
187
+ RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(requestConfig.body, contentType))
188
+ RequestMethod.POST -> Request.Builder().url(url).post(requestBody(requestConfig.body, contentType))
189
+ RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null)
190
+ }.apply {
191
+ headers.forEach { header -> addHeader(header.key, header.value) }
192
+ }.build()
193
+
194
+ val response = client.newCall(request).execute()
195
+
196
+ val accept = response.header(ContentType)?.substringBefore(";")?.lowercase(Locale.getDefault())
197
+
198
+ // TODO: handle specific mapping types. e.g. Map<int, Class<?>>
199
+ return when {
200
+ response.isRedirect -> Redirection(
201
+ response.code,
202
+ response.headers.toMultimap()
203
+ )
204
+
205
+ response.isInformational -> Informational(
206
+ response.message,
207
+ response.code,
208
+ response.headers.toMultimap()
209
+ )
210
+
211
+ response.isSuccessful -> Success(
212
+ responseBody(response.body, accept),
213
+ response.code,
214
+ response.headers.toMultimap()
215
+ )
216
+
217
+ response.isClientError -> ClientError(
218
+ response.message,
219
+ response.body?.string(),
220
+ response.code,
221
+ response.headers.toMultimap()
222
+ )
223
+
224
+ else -> ServerError(
225
+ response.message,
226
+ response.body?.string(),
227
+ response.code,
228
+ response.headers.toMultimap()
229
+ )
230
+ }
231
+ }
232
+
233
+ protected fun parameterToString(value: Any?): String = when (value) {
234
+ null -> ""
235
+ is Array<*> -> toMultiValue(value, "csv").toString()
236
+ is Iterable<*> -> toMultiValue(value, "csv").toString()
237
+ is OffsetDateTime, is OffsetTime, is LocalDateTime, is LocalDate, is LocalTime ->
238
+ parseDateToQueryString(value)
239
+
240
+ else -> value.toString()
241
+ }
242
+
243
+ protected fun parseDateToQueryString(value: Any): String {
244
+ /*
245
+ .replace("\"", "") converts the json object string to an actual string for the query parameter.
246
+ The moshi or gson adapter allows a more generic solution instead of trying to use a native
247
+ formatter. It also easily allows to provide a simple way to define a custom date format pattern
248
+ inside a gson/moshi adapter.
249
+ */
250
+ return objectMapper.writeValueAsString(value).replace("\"", "")
251
+ }
252
+ }
@@ -1,43 +1,43 @@
1
- package @PACKAGE_NAME@
2
-
3
- enum class ResponseType {
4
- Success, Informational, Redirection, ClientError, ServerError
5
- }
6
-
7
- interface Response
8
-
9
- abstract class ApiResponse<T>(val responseType: ResponseType) : Response {
10
- abstract val statusCode: Int
11
- abstract val headers: Map<String, List<String>>
12
- }
13
-
14
- class Success<T>(
15
- val data: T,
16
- override val statusCode: Int = -1,
17
- override val headers: Map<String, List<String>> = mapOf()
18
- ) : ApiResponse<T>(ResponseType.Success)
19
-
20
- class Informational<T>(
21
- val statusText: String,
22
- override val statusCode: Int = -1,
23
- override val headers: Map<String, List<String>> = mapOf()
24
- ) : ApiResponse<T>(ResponseType.Informational)
25
-
26
- class Redirection<T>(
27
- override val statusCode: Int = -1,
28
- override val headers: Map<String, List<String>> = mapOf()
29
- ) : ApiResponse<T>(ResponseType.Redirection)
30
-
31
- class ClientError<T>(
32
- val message: String? = null,
33
- val body: Any? = null,
34
- override val statusCode: Int = -1,
35
- override val headers: Map<String, List<String>> = mapOf()
36
- ) : ApiResponse<T>(ResponseType.ClientError)
37
-
38
- class ServerError<T>(
39
- val message: String? = null,
40
- val body: Any? = null,
41
- override val statusCode: Int = -1,
42
- override val headers: Map<String, List<String>>
43
- ) : ApiResponse<T>(ResponseType.ServerError)
1
+ package @PACKAGE_NAME@
2
+
3
+ enum class ResponseType {
4
+ Success, Informational, Redirection, ClientError, ServerError
5
+ }
6
+
7
+ interface Response
8
+
9
+ abstract class ApiResponse<T>(val responseType: ResponseType) : Response {
10
+ abstract val statusCode: Int
11
+ abstract val headers: Map<String, List<String>>
12
+ }
13
+
14
+ class Success<T>(
15
+ val data: T,
16
+ override val statusCode: Int = -1,
17
+ override val headers: Map<String, List<String>> = mapOf()
18
+ ) : ApiResponse<T>(ResponseType.Success)
19
+
20
+ class Informational<T>(
21
+ val statusText: String,
22
+ override val statusCode: Int = -1,
23
+ override val headers: Map<String, List<String>> = mapOf()
24
+ ) : ApiResponse<T>(ResponseType.Informational)
25
+
26
+ class Redirection<T>(
27
+ override val statusCode: Int = -1,
28
+ override val headers: Map<String, List<String>> = mapOf()
29
+ ) : ApiResponse<T>(ResponseType.Redirection)
30
+
31
+ class ClientError<T>(
32
+ val message: String? = null,
33
+ val body: Any? = null,
34
+ override val statusCode: Int = -1,
35
+ override val headers: Map<String, List<String>> = mapOf()
36
+ ) : ApiResponse<T>(ResponseType.ClientError)
37
+
38
+ class ServerError<T>(
39
+ val message: String? = null,
40
+ val body: Any? = null,
41
+ override val statusCode: Int = -1,
42
+ override val headers: Map<String, List<String>>
43
+ ) : ApiResponse<T>(ResponseType.ServerError)
@@ -1,21 +1,21 @@
1
- @file:Suppress("unused")
2
-
3
- package @PACKAGE_NAME@
4
-
5
- import java.lang.RuntimeException
6
-
7
- open class ClientException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) :
8
- RuntimeException(message) {
9
-
10
- companion object {
11
- private const val serialVersionUID: Long = 123L
12
- }
13
- }
14
-
15
- open class ServerException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) :
16
- RuntimeException(message) {
17
-
18
- companion object {
19
- private const val serialVersionUID: Long = 456L
20
- }
21
- }
1
+ @file:Suppress("unused")
2
+
3
+ package @PACKAGE_NAME@
4
+
5
+ import java.lang.RuntimeException
6
+
7
+ open class ClientException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) :
8
+ RuntimeException(message) {
9
+
10
+ companion object {
11
+ private const val serialVersionUID: Long = 123L
12
+ }
13
+ }
14
+
15
+ open class ServerException(message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) :
16
+ RuntimeException(message) {
17
+
18
+ companion object {
19
+ private const val serialVersionUID: Long = 456L
20
+ }
21
+ }
@@ -1,11 +1,11 @@
1
- package @PACKAGE_NAME@
2
-
3
- /**
4
- * Defines a config object for a given part of a multi-part request.
5
- * NOTE: Headers is a Map<String,String> because rfc2616 defines
6
- * multi-valued headers as csv-only.
7
- */
8
- data class PartConfig<T>(
9
- val headers: MutableMap<String, String> = mutableMapOf(),
10
- val body: T? = null
11
- )
1
+ package @PACKAGE_NAME@
2
+
3
+ /**
4
+ * Defines a config object for a given part of a multi-part request.
5
+ * NOTE: Headers is a Map<String,String> because rfc2616 defines
6
+ * multi-valued headers as csv-only.
7
+ */
8
+ data class PartConfig<T>(
9
+ val headers: MutableMap<String, String> = mutableMapOf(),
10
+ val body: T? = null
11
+ )