11package io.appwrite
22
3- import com.google.gson.Gson
3+ import com.google.gson.GsonBuilder
4+ import com.google.gson.reflect.TypeToken
45import 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
78import kotlinx.coroutines.CoroutineScope
89import kotlinx.coroutines.Dispatchers
910import kotlinx.coroutines.Job
@@ -15,6 +16,7 @@ import okhttp3.MediaType.Companion.toMediaType
1516import okhttp3.RequestBody.Companion.asRequestBody
1617import okhttp3.RequestBody.Companion.toRequestBody
1718import okhttp3.logging.HttpLoggingInterceptor
19+ import java.io.BufferedInputStream
1820import java.io.BufferedReader
1921import java.io.File
2022import java.io.IOException
@@ -38,20 +40,26 @@ class Client @JvmOverloads constructor(
3840
3941 private val job = Job ()
4042
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
4249
4350 private val headers: MutableMap <String , String >
44-
51+
4552 val config: MutableMap <String , String >
4653
54+
4755 init {
4856 headers = mutableMapOf (
4957 " content-type" to " application/json" ,
5058 " x-sdk-version" to " appwrite:kotlin:0.1.1" ,
5159 " x-appwrite-response-format" to " 0.11.0"
5260 )
5361 config = mutableMapOf ()
54-
62+
5563 setSelfSigned(selfSigned)
5664 }
5765
@@ -115,10 +123,10 @@ class Client @JvmOverloads constructor(
115123
116124 /* *
117125 * Set self Signed
118- *
126+ *
119127 * @param status
120128 *
121- * @return this
129+ * @return this
122130 */
123131 fun setSelfSigned (status : Boolean ): Client {
124132 selfSigned = status
@@ -134,9 +142,12 @@ class Client @JvmOverloads constructor(
134142 try {
135143 // Create a trust manager that does not validate certificate chains
136144 val trustAllCerts = arrayOf<TrustManager >(
145+ @Suppress(" CustomX509TrustManager" )
137146 object : X509TrustManager {
147+ @Suppress(" TrustAllX509TrustManager" )
138148 override fun checkClientTrusted (chain : Array <X509Certificate >, authType : String ) {
139149 }
150+ @Suppress(" TrustAllX509TrustManager" )
140151 override fun checkServerTrusted (chain : Array <X509Certificate >, authType : String ) {
141152 }
142153 override fun getAcceptedIssuers (): Array <X509Certificate > {
@@ -175,11 +186,11 @@ class Client @JvmOverloads constructor(
175186
176187 /* *
177188 * Add Header
178- *
189+ *
179190 * @param key
180191 * @param value
181192 *
182- * @return this
193+ * @return this
183194 */
184195 fun addHeader (key : String , value : String ): Client {
185196 headers[key] = value
@@ -188,22 +199,23 @@ class Client @JvmOverloads constructor(
188199
189200 /* *
190201 * Send the HTTP request
191- *
202+ *
192203 * @param method
193204 * @param path
194205 * @param headers
195206 * @param params
196207 *
197- * @return [Response]
208+ * @return [Response]
198209 */
199210 @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 {
207219 val filteredParams = params.filterValues { it != null }
208220
209221 val requestHeaders = this .headers.toHeaders().newBuilder()
@@ -238,7 +250,7 @@ class Client @JvmOverloads constructor(
238250 .get()
239251 .build()
240252
241- return awaitResponse(request)
253+ return awaitResponse(request, responseType, convert )
242254 }
243255
244256 val body = if (MultipartBody .FORM .toString() == headers[" content-type" ]) {
@@ -266,7 +278,7 @@ class Client @JvmOverloads constructor(
266278 }
267279 builder.build()
268280 } else {
269- Gson () .toJson(filteredParams)
281+ gson .toJson(filteredParams)
270282 .toRequestBody(" application/json" .toMediaType())
271283 }
272284
@@ -276,21 +288,24 @@ class Client @JvmOverloads constructor(
276288 .method(method, body)
277289 .build()
278290
279- return awaitResponse(request)
291+ return awaitResponse(request, responseType, convert )
280292 }
281293
282294 /* *
283295 * Await Response
284- *
285- * @param method
286- * @param path
287- * @param headers
288- * @param params
289296 *
290- * @return [Response]
297+ * @param request
298+ * @param responseType
299+ * @param convert
300+ *
301+ * @return [T]
291302 */
292303 @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 > {
294309 http.newCall(request).enqueue(object : Callback {
295310 override fun onFailure (call : Call , e : IOException ) {
296311 if (it.isCancelled) {
@@ -299,27 +314,54 @@ class Client @JvmOverloads constructor(
299314 it.cancel(e)
300315 }
301316
317+ @Suppress(" UNCHECKED_CAST" )
302318 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()
312326 } else {
313- Error (bodyString , response.code)
327+ AppwriteException (body , response.code)
314328 }
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
321357 }
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+ )
323365 }
324366 })
325367 }
0 commit comments