Skip to content

Commit f5a621c

Browse files
Merge pull request #300 from appwrite/feat-kotlin-response-models
Feat kotlin response models
2 parents ca6022f + e116429 commit f5a621c

File tree

13 files changed

+309
-116
lines changed

13 files changed

+309
-116
lines changed

src/SDK/Language/Kotlin.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,13 @@ public function getFiles()
354354
],
355355
[
356356
'scope' => 'default',
357-
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/services/BaseService.kt',
357+
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/json/PreciseNumberAdapter.kt',
358+
'template' => '/kotlin/src/main/kotlin/io/appwrite/json/PreciseNumberAdapter.kt.twig',
359+
'minify' => false,
360+
],
361+
[
362+
'scope' => 'default',
363+
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/services/Service.kt',
358364
'template' => '/kotlin/src/main/kotlin/io/appwrite/services/Service.kt.twig',
359365
'minify' => false,
360366
],
@@ -363,7 +369,13 @@ public function getFiles()
363369
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/services/{{service.name | caseUcfirst}}.kt',
364370
'template' => '/kotlin/src/main/kotlin/io/appwrite/services/ServiceTemplate.kt.twig',
365371
'minify' => false,
366-
]
372+
],
373+
[
374+
'scope' => 'definition',
375+
'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/models/{{ definition.name | caseUcfirst }}.kt',
376+
'template' => '/kotlin/src/main/kotlin/io/appwrite/models/Model.kt.twig',
377+
'minify' => false,
378+
],
367379
];
368380
}
369381
}

templates/kotlin/.github/workflows/publish.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ jobs:
1212
steps:
1313
- name: Check out code
1414
uses: actions/checkout@v2
15-
- name: Set up JDK 1.8
15+
- name: Set up JDK 11
1616
uses: actions/setup-java@v1
1717
with:
18-
java-version: 1.8
18+
java-version: 11
1919
# Base64 decodes and pipes the GPG key content into the secret file
2020
- name: Prepare environment
2121
env:
@@ -41,4 +41,4 @@ jobs:
4141
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
4242
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
4343
SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }}
44-
SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
44+
SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}

templates/kotlin/build.gradle.twig

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
plugins {
2-
id 'org.jetbrains.kotlin.jvm' version '1.4.32'
2+
id 'org.jetbrains.kotlin.jvm' version '1.5.31'
33
id 'java-library'
44
}
55

6-
76
ext {
87
PUBLISH_GROUP_ID = '{{ sdk.namespace | caseDot }}'
98
PUBLISH_ARTIFACT_ID = '{{ sdk.gitRepoName | caseDash }}'
@@ -27,12 +26,12 @@ repositories {
2726
}
2827

2928
dependencies {
30-
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3")
29+
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
3130
api(platform("com.squareup.okhttp3:okhttp-bom:4.9.0"))
3231
api("com.squareup.okhttp3:okhttp")
3332
implementation("com.squareup.okhttp3:okhttp-urlconnection")
3433
implementation("com.squareup.okhttp3:logging-interceptor")
35-
implementation("com.google.code.gson:gson:2.8.5")
34+
implementation("com.google.code.gson:gson:2.8.7")
3635

3736
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
3837
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
21
rootProject.name = '{{ sdk.gitRepoName | caseDash }}'
32

templates/kotlin/src/main/kotlin/io/appwrite/Client.kt.twig

Lines changed: 87 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package {{ sdk.namespace | caseDot }}
22

3-
import com.google.gson.Gson
3+
import com.google.gson.GsonBuilder
4+
import com.google.gson.reflect.TypeToken
45
import {{ sdk.namespace | caseDot }}.exceptions.{{ spec.title | caseUcfirst }}Exception
56
import {{ sdk.namespace | caseDot }}.extensions.fromJson
7+
import {{ sdk.namespace | caseDot }}.json.PreciseNumberAdapter
68
import kotlinx.coroutines.CoroutineScope
79
import kotlinx.coroutines.Dispatchers
810
import kotlinx.coroutines.Job
@@ -14,6 +16,7 @@ import okhttp3.MediaType.Companion.toMediaType
1416
import okhttp3.RequestBody.Companion.asRequestBody
1517
import okhttp3.RequestBody.Companion.toRequestBody
1618
import okhttp3.logging.HttpLoggingInterceptor
19+
import java.io.BufferedInputStream
1720
import java.io.BufferedReader
1821
import java.io.File
1922
import java.io.IOException
@@ -37,12 +40,18 @@ class Client @JvmOverloads constructor(
3740

3841
private val job = Job()
3942

40-
private lateinit var http: OkHttpClient
43+
private val gson = GsonBuilder().registerTypeAdapter(
44+
object : TypeToken<Map<String, Any>>(){}.type,
45+
PreciseNumberAdapter()
46+
).create()
47+
48+
lateinit var http: OkHttpClient
4149

4250
private val headers: MutableMap<String, String>
43-
51+
4452
val config: MutableMap<String, String>
4553

54+
4655
init {
4756
headers = mutableMapOf(
4857
"content-type" to "application/json",
@@ -54,7 +63,7 @@ class Client @JvmOverloads constructor(
5463

5564
)
5665
config = mutableMapOf()
57-
66+
5867
setSelfSigned(selfSigned)
5968
}
6069

@@ -79,10 +88,10 @@ class Client @JvmOverloads constructor(
7988
{% endfor %}
8089
/**
8190
* Set self Signed
82-
*
91+
*
8392
* @param status
8493
*
85-
* @return this
94+
* @return this
8695
*/
8796
fun setSelfSigned(status: Boolean): Client {
8897
selfSigned = status
@@ -98,9 +107,12 @@ class Client @JvmOverloads constructor(
98107
try {
99108
// Create a trust manager that does not validate certificate chains
100109
val trustAllCerts = arrayOf<TrustManager>(
110+
@Suppress("CustomX509TrustManager")
101111
object : X509TrustManager {
112+
@Suppress("TrustAllX509TrustManager")
102113
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
103114
}
115+
@Suppress("TrustAllX509TrustManager")
104116
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
105117
}
106118
override fun getAcceptedIssuers(): Array<X509Certificate> {
@@ -139,11 +151,11 @@ class Client @JvmOverloads constructor(
139151

140152
/**
141153
* Add Header
142-
*
154+
*
143155
* @param key
144156
* @param value
145157
*
146-
* @return this
158+
* @return this
147159
*/
148160
fun addHeader(key: String, value: String): Client {
149161
headers[key] = value
@@ -152,22 +164,23 @@ class Client @JvmOverloads constructor(
152164

153165
/**
154166
* Send the HTTP request
155-
*
167+
*
156168
* @param method
157169
* @param path
158170
* @param headers
159171
* @param params
160172
*
161-
* @return [Response]
173+
* @return [Response]
162174
*/
163175
@Throws({{ spec.title | caseUcfirst }}Exception::class)
164-
suspend fun call(
165-
method: String,
166-
path: String,
167-
headers: Map<String, String> = mapOf(),
168-
params: Map<String, Any?> = mapOf()
169-
): Response {
170-
176+
suspend fun <T> call(
177+
method: String,
178+
path: String,
179+
headers: Map<String, String> = mapOf(),
180+
params: Map<String, Any?> = mapOf(),
181+
responseType: Class<T>,
182+
convert: ((Map<String, Any,>) -> T)? = null
183+
): T {
171184
val filteredParams = params.filterValues { it != null }
172185

173186
val requestHeaders = this.headers.toHeaders().newBuilder()
@@ -202,7 +215,7 @@ class Client @JvmOverloads constructor(
202215
.get()
203216
.build()
204217

205-
return awaitResponse(request)
218+
return awaitResponse(request, responseType, convert)
206219
}
207220

208221
val body = if (MultipartBody.FORM.toString() == headers["content-type"]) {
@@ -230,7 +243,7 @@ class Client @JvmOverloads constructor(
230243
}
231244
builder.build()
232245
} else {
233-
Gson().toJson(filteredParams)
246+
gson.toJson(filteredParams)
234247
.toRequestBody("application/json".toMediaType())
235248
}
236249

@@ -240,21 +253,24 @@ class Client @JvmOverloads constructor(
240253
.method(method, body)
241254
.build()
242255

243-
return awaitResponse(request)
256+
return awaitResponse(request, responseType, convert)
244257
}
245258

246259
/**
247260
* Await Response
248-
*
249-
* @param method
250-
* @param path
251-
* @param headers
252-
* @param params
253261
*
254-
* @return [Response]
262+
* @param request
263+
* @param responseType
264+
* @param convert
265+
*
266+
* @return [T]
255267
*/
256268
@Throws({{ spec.title | caseUcfirst }}Exception::class)
257-
private suspend fun awaitResponse(request: Request) = suspendCancellableCoroutine<Response> {
269+
private suspend fun <T> awaitResponse(
270+
request: Request,
271+
responseType: Class<T>,
272+
convert: ((Map<String, Any,>) -> T)? = null
273+
) = suspendCancellableCoroutine<T> {
258274
http.newCall(request).enqueue(object : Callback {
259275
override fun onFailure(call: Call, e: IOException) {
260276
if (it.isCancelled) {
@@ -263,23 +279,55 @@ class Client @JvmOverloads constructor(
263279
it.cancel(e)
264280
}
265281

282+
@Suppress("UNCHECKED_CAST")
266283
override fun onResponse(call: Call, response: Response) {
267-
if (response.code >= 400) {
268-
val bodyString = response.body
269-
?.charStream()
270-
?.buffered()
271-
?.use(BufferedReader::readText) ?: ""
272-
273-
val contentType: String = response.headers["content-type"] ?: ""
274-
val error = if (contentType.contains("application/json", ignoreCase = true)) {
275-
bodyString.fromJson()
284+
if (!response.isSuccessful) {
285+
val body = response.body!!
286+
.charStream()
287+
.buffered()
288+
.use(BufferedReader::readText)
289+
val error = if (response.headers["content-type"]?.contains("application/json") == true) {
290+
body.fromJson()
276291
} else {
277-
{{ spec.title | caseUcfirst }}Exception(bodyString, response.code)
292+
{{ spec.title | caseUcfirst }}Exception(body, response.code)
278293
}
279294
it.cancel(error)
295+
return
296+
}
297+
when {
298+
responseType == Boolean::class.java -> {
299+
it.resume(true as T)
300+
return
301+
}
302+
responseType == ByteArray::class.java -> {
303+
it.resume(response.body!!
304+
.byteStream()
305+
.buffered()
306+
.use(BufferedInputStream::readBytes) as T
307+
)
308+
return
309+
}
310+
response.body == null -> {
311+
it.resume(true as T)
312+
return
313+
}
314+
}
315+
val body = response.body!!
316+
.charStream()
317+
.buffered()
318+
.use(BufferedReader::readText)
319+
if (body.isEmpty()) {
320+
it.resume(true as T)
321+
return
280322
}
281-
it.resume(response)
323+
val map = gson.fromJson<Map<String, Any>>(
324+
body,
325+
object : TypeToken<Map<String, Any>>(){}.type
326+
)
327+
it.resume(
328+
convert?.invoke(map) ?: map as T
329+
)
282330
}
283331
})
284332
}
285-
}
333+
}

templates/kotlin/src/main/kotlin/io/appwrite/extensions/JsonExtensions.kt.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ inline fun <reified T> Any.tryJsonCast(): T? = try {
3131
} catch (ex: Exception) {
3232
ex.printStackTrace()
3333
null
34-
}
34+
}

0 commit comments

Comments
 (0)