1
1
package io.appwrite
2
2
3
- import com.google.gson.Gson
3
+ import com.google.gson.GsonBuilder
4
+ import com.google.gson.reflect.TypeToken
4
5
import io.appwrite.exceptions.AppwriteException
5
- import io.appwrite.extensions.JsonExtensions. fromJson
6
- import io.appwrite.models.Error
6
+ import io.appwrite.extensions.fromJson
7
+ import io.appwrite.json.PreciseNumberAdapter
7
8
import kotlinx.coroutines.CoroutineScope
8
9
import kotlinx.coroutines.Dispatchers
9
10
import kotlinx.coroutines.Job
@@ -15,6 +16,7 @@ import okhttp3.MediaType.Companion.toMediaType
15
16
import okhttp3.RequestBody.Companion.asRequestBody
16
17
import okhttp3.RequestBody.Companion.toRequestBody
17
18
import okhttp3.logging.HttpLoggingInterceptor
19
+ import java.io.BufferedInputStream
18
20
import java.io.BufferedReader
19
21
import java.io.File
20
22
import java.io.IOException
@@ -38,20 +40,26 @@ class Client @JvmOverloads constructor(
38
40
39
41
private val job = Job ()
40
42
41
- 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
42
49
43
50
private val headers: MutableMap <String , String >
44
-
51
+
45
52
val config: MutableMap <String , String >
46
53
54
+
47
55
init {
48
56
headers = mutableMapOf (
49
57
" content-type" to " application/json" ,
50
58
" x-sdk-version" to " appwrite:kotlin:0.1.1" ,
51
59
" x-appwrite-response-format" to " 0.11.0"
52
60
)
53
61
config = mutableMapOf ()
54
-
62
+
55
63
setSelfSigned(selfSigned)
56
64
}
57
65
@@ -115,10 +123,10 @@ class Client @JvmOverloads constructor(
115
123
116
124
/* *
117
125
* Set self Signed
118
- *
126
+ *
119
127
* @param status
120
128
*
121
- * @return this
129
+ * @return this
122
130
*/
123
131
fun setSelfSigned (status : Boolean ): Client {
124
132
selfSigned = status
@@ -134,9 +142,12 @@ class Client @JvmOverloads constructor(
134
142
try {
135
143
// Create a trust manager that does not validate certificate chains
136
144
val trustAllCerts = arrayOf<TrustManager >(
145
+ @Suppress(" CustomX509TrustManager" )
137
146
object : X509TrustManager {
147
+ @Suppress(" TrustAllX509TrustManager" )
138
148
override fun checkClientTrusted (chain : Array <X509Certificate >, authType : String ) {
139
149
}
150
+ @Suppress(" TrustAllX509TrustManager" )
140
151
override fun checkServerTrusted (chain : Array <X509Certificate >, authType : String ) {
141
152
}
142
153
override fun getAcceptedIssuers (): Array <X509Certificate > {
@@ -175,11 +186,11 @@ class Client @JvmOverloads constructor(
175
186
176
187
/* *
177
188
* Add Header
178
- *
189
+ *
179
190
* @param key
180
191
* @param value
181
192
*
182
- * @return this
193
+ * @return this
183
194
*/
184
195
fun addHeader (key : String , value : String ): Client {
185
196
headers[key] = value
@@ -188,22 +199,23 @@ class Client @JvmOverloads constructor(
188
199
189
200
/* *
190
201
* Send the HTTP request
191
- *
202
+ *
192
203
* @param method
193
204
* @param path
194
205
* @param headers
195
206
* @param params
196
207
*
197
- * @return [Response]
208
+ * @return [Response]
198
209
*/
199
210
@Throws(AppwriteException ::class )
200
- suspend fun call (
201
- method : String ,
202
- path : String ,
203
- headers : Map <String , String > = mapOf(),
204
- params : Map <String , Any ?> = mapOf()
205
- ): Response {
206
-
211
+ suspend fun <T > call (
212
+ method : String ,
213
+ path : String ,
214
+ headers : Map <String , String > = mapOf(),
215
+ params : Map <String , Any ?> = mapOf(),
216
+ responseType : Class <T >,
217
+ convert : ((Map <String , Any ,>) -> T )? = null
218
+ ): T {
207
219
val filteredParams = params.filterValues { it != null }
208
220
209
221
val requestHeaders = this .headers.toHeaders().newBuilder()
@@ -238,7 +250,7 @@ class Client @JvmOverloads constructor(
238
250
.get()
239
251
.build()
240
252
241
- return awaitResponse(request)
253
+ return awaitResponse(request, responseType, convert )
242
254
}
243
255
244
256
val body = if (MultipartBody .FORM .toString() == headers[" content-type" ]) {
@@ -266,7 +278,7 @@ class Client @JvmOverloads constructor(
266
278
}
267
279
builder.build()
268
280
} else {
269
- Gson () .toJson(filteredParams)
281
+ gson .toJson(filteredParams)
270
282
.toRequestBody(" application/json" .toMediaType())
271
283
}
272
284
@@ -276,21 +288,24 @@ class Client @JvmOverloads constructor(
276
288
.method(method, body)
277
289
.build()
278
290
279
- return awaitResponse(request)
291
+ return awaitResponse(request, responseType, convert )
280
292
}
281
293
282
294
/* *
283
295
* Await Response
284
- *
285
- * @param method
286
- * @param path
287
- * @param headers
288
- * @param params
289
296
*
290
- * @return [Response]
297
+ * @param request
298
+ * @param responseType
299
+ * @param convert
300
+ *
301
+ * @return [T]
291
302
*/
292
303
@Throws(AppwriteException ::class )
293
- private suspend fun awaitResponse (request : Request ) = suspendCancellableCoroutine<Response > {
304
+ private suspend fun <T > awaitResponse (
305
+ request : Request ,
306
+ responseType : Class <T >,
307
+ convert : ((Map <String , Any ,>) -> T )? = null
308
+ ) = suspendCancellableCoroutine<T > {
294
309
http.newCall(request).enqueue(object : Callback {
295
310
override fun onFailure (call : Call , e : IOException ) {
296
311
if (it.isCancelled) {
@@ -299,27 +314,54 @@ class Client @JvmOverloads constructor(
299
314
it.cancel(e)
300
315
}
301
316
317
+ @Suppress(" UNCHECKED_CAST" )
302
318
override fun onResponse (call : Call , response : Response ) {
303
- if (response.code >= 400 ) {
304
- val bodyString = response.body
305
- ?.charStream()
306
- ?.buffered()
307
- ?.use(BufferedReader ::readText) ? : " "
308
-
309
- val contentType: String = response.headers[" content-type" ] ? : " "
310
- val error = if (contentType.contains(" application/json" , ignoreCase = true )) {
311
- bodyString.fromJson(Error ::class .java)
319
+ if (! response.isSuccessful) {
320
+ val body = response.body!!
321
+ .charStream()
322
+ .buffered()
323
+ .use(BufferedReader ::readText)
324
+ val error = if (response.headers[" content-type" ]?.contains(" application/json" ) == true ) {
325
+ body.fromJson()
312
326
} else {
313
- Error (bodyString , response.code)
327
+ AppwriteException (body , response.code)
314
328
}
315
-
316
- it.cancel(AppwriteException (
317
- error.message,
318
- error.code,
319
- bodyString
320
- ))
329
+ it.cancel(error)
330
+ return
331
+ }
332
+ when {
333
+ responseType == Boolean ::class .java -> {
334
+ it.resume(true as T )
335
+ return
336
+ }
337
+ responseType == ByteArray ::class .java -> {
338
+ it.resume(response.body!!
339
+ .byteStream()
340
+ .buffered()
341
+ .use(BufferedInputStream ::readBytes) as T
342
+ )
343
+ return
344
+ }
345
+ response.body == null -> {
346
+ it.resume(true as T )
347
+ return
348
+ }
349
+ }
350
+ val body = response.body!!
351
+ .charStream()
352
+ .buffered()
353
+ .use(BufferedReader ::readText)
354
+ if (body.isEmpty()) {
355
+ it.resume(true as T )
356
+ return
321
357
}
322
- it.resume(response)
358
+ val map = gson.fromJson<Map <String , Any >>(
359
+ body,
360
+ object : TypeToken <Map <String , Any >>(){}.type
361
+ )
362
+ it.resume(
363
+ convert?.invoke(map) ? : map as T
364
+ )
323
365
}
324
366
})
325
367
}
0 commit comments