I have deployed a firebase cloud function (http function) which i could access successfully using postman tool, but when i try to access through client sdk from my android application i always get a permission denied error:
W/System.err: com.google.firebase.functions.FirebaseFunctionsException: PERMISSION_DENIED
04-19 15:18:00.258 3118-3118/com.abc.xyz W/System.err: at com.google.firebase.functions.FirebaseFunctions$2.onResponse(com.google.firebase:firebase-functions##16.3.0:273)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Here is the code i used to access the function
private fun saveInstallationId(key: String, userId : String, installationKey : String) : Task<String> {
val data = hashMapOf(
"key" to key,
"installationId" to installationKey,
"userId" to userId
)
return functions
.getHttpsCallable("setInstallationId")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
result
}
I think i am missing some headers, is it possible to set header parameters using client sdk?
Related
I'm developing an app that fetch exchange rate data for various currencies, but I'm facing an error after adding dynamic patameters to the value of GET Request. Here is my code:
interface CurrenciesApiService {
#GET("convert")
suspend fun getText(#Query("q") from_to: String, #Query("compact") compact: String, #Query("apiKey") key: String): Response<Currencies> {
return RetrofitInstances.retrofitService.getText(from_to, compact, key)
}
Retrofit Object:
object RetrofitInstances {
private val retrofit =
Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(
BASE_URL
).build()
val retrofitService: CurrenciesApiService by lazy { retrofit.create(CurrenciesApiService::class.java) }
}
BASE URL:
companion object {
const val BASE_URL = "https://free.currconv.com/api/v7/"
}
the Log:
2022-04-09 09:58:39.713 3201-3201/com.example.myapplication D/response: TRY
2022-04-09 09:58:39.713 3201-3201/com.example.myapplication D/response: USD
2022-04-09 09:58:39.713 3201-3201/com.example.myapplication D/response: TRY_USD
2022-04-09 10:41:25.854 7036-7036/com.example.myapplication D/response: Response{protocol=h2, code=200, message=, url=https://free.currconv.com/api/v7/convert?q=TRY_USD&compact=ultra&apiKey=e0ba65b5ae1bf1b0019c}
and the last line is the query
The visual result:
Ok! I solved this issue by shifting to Scalar converter. I fetched the response as a string then I converted it to JSONObject, thus you can parse the value easily.
When I would like to use #Get I get 401 HTTP error which means I am not authorized.
Before that I make POST on /authentication and POST on /authorization and it works I am successfully logged in.
The weird thing for me is I get two tokens, can someone explain why?
class AuthInterceptor(context: Context) : Interceptor {
private val sessionManager = SessionManager(context)
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
// If token has been saved, add it to the request
sessionManager.fetchAuthToken()?.let {
requestBuilder.addHeader("Authorization", "Bearer $it")
Log.d("authorization", "$it")
// on authorization I get two tokens, after click Login first appears, 2 seconds later next one
}
return chain.proceed(requestBuilder.build())
}
}
Api
// I think I do not need add here #Headers if I use method in previous class requestBuilder.addHeader~
interface Api {
#GET("device")
suspend fun getDetails(#Query("id") id: String): CameraResponse
}
authApi
interface AuthApi {
#POST("authenticate")
fun login(#Body request: User): Call<AuthResponse>
#POST("authorize")
fun authorize(#Body request: AuthResponse): Call<AuthorizeResponse>
}
error from stacktrace
W/System.err: retrofit2.HttpException: HTTP 401
at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:161)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:923)
My documentation
curl -X GET https://myApiDocumentation/device -d "id=[MY_ID]" -H "Authentication: [API_KEY]" --cookie "auth_key=[AUTH_KEY]" -G
If you need more code from program, here is my post which is fixed and another problem occurs...
Why it gets HTTP 404 error with #GET retrofit?
I want to do an async-await to my Retrofit request.
#GET("{companyId}/{filename}")
#Streaming
suspend fun getFilePart(
#Path(value = "companyId") companyId: Int,
#Path(value = "filename") filename: String,
#QueryMap queryMap: Map<String, String>
): Deferred<ResponseBody>
and when i call it from a CoroutineScope i have
val deferredList = pendingFiles.map {
async(Dispatchers.IO) {
try {
// runs in parallel in background thread
// Consider i have the data parameters....
apiManager.mApiService(base).getFilePart(it.companyId, fileName, urlQueryMap)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
So the request returns 200 which means that it is successful, and i see the Logging with the File part data. But i get the following exception
W/System.err: java.lang.RuntimeException: Unable to invoke no-args constructor for com.google.firebase.inject.Deferred<okhttp3.ResponseBody>. Registering an InstanceCreator with Gson for this type may fix this problem.
W/System.err: at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:228)
W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
W/System.err: at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:40)
W/System.err: at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
W/System.err: at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
W/System.err: at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
W/System.err: at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:520)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:920)
W/System.err: Caused by: java.lang.UnsupportedOperationException: Interface can't be instantiated! Interface name: com.google.firebase.inject.Deferred
W/System.err: at com.google.gson.internal.UnsafeAllocator.assertInstantiable(UnsafeAllocator.java:117)
W/System.err: at com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:49)
W/System.err: at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
W/System.err: ... 9 more
If your function is suspend, it shouldn't return Deferred. Just declare it this way:
#GET("{companyId}/{filename}")
#Streaming
suspend fun getFilePart(
#Path(value = "companyId") companyId: Int,
#Path(value = "filename") filename: String,
#QueryMap queryMap: Map<String, String>
): ResponseBody // no Deferred here
Functions with suspend keyword are used like regular functions (that's the beauty of coroutines), so they return a regular type. You actually start asynchronous code when you use coroutine builders like you do with async - this is the one that returns Deferred<T> and is not suspending.
The idea: There's an endpoint I'm trying to consume on Android. After calling it, the endpoint will return HTTP 202 (empty body, there's no payload at all) for a couple of times and when the data is ready it will return HTTP 200 with the data.
What I want to do is be able to parse the 202 response, but since it does not have a payload I'll just check the status and poll again if needed and once I get the 200 OK, I'll parse and use the data.
The part where I parse and use the 200 OK response is working, but parsing the 202 response does not work, I get the Exception (see below).
I know there are several approaches to this:
using Void as the return type,
using ResponseBody as the return type,
using a Converter.Factory.
I've created a converter class for this:
internal val nullOnEmptyConverterFactory = object : Converter.Factory() {
fun converterFactory() = this
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
) = object : Converter<ResponseBody, Any?> {
val nextResponseBodyConverter =
retrofit.nextResponseBodyConverter<Any?>(converterFactory(), type, annotations)
override fun convert(value: ResponseBody) =
if (value.contentLength() != 0L) nextResponseBodyConverter.convert(value) else null
}
}
This is how I use it:
return Retrofit.Builder()
.client(client)
.addConverterFactory(nullOnEmptyConverterFactory)
.addConverterFactory(ScalarsConverterFactory.create()) // used for jsonp
.addConverterFactory(GsonConverterFactory.create(factory))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(ApiService.getBaseUrl())
.build()
And this is the error I get:
error: java.io.EOFException
at okio.RealBufferedSource.require(RealBufferedSource.kt:55)
at okio.GzipSource.consumeHeader(GzipSource.kt:104)
at okio.GzipSource.read(GzipSource.kt:62)
at okio.RealBufferedSource.read(RealBufferedSource.kt:41)
at okio.ForwardingSource.read(ForwardingSource.kt:29)
at retrofit2.OkHttpCall$ExceptionCatchingResponseBody$1.read(OkHttpCall.java:288)
at okio.RealBufferedSource.select(RealBufferedSource.kt:93)
at okhttp3.internal.Util.readBomAsCharset(Util.kt:256)
at okhttp3.ResponseBody$BomAwareReader.read(ResponseBody.kt:208)
at com.google.gson.stream.JsonReader.fillBuffer(JsonReader.java:1295)
at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1333)
at com.google.gson.stream.JsonReader.consumeNonExecutePrefix(JsonReader.java:1576)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:534)
at com.google.gson.stream.JsonReader.peek(JsonReader.java:425)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:207)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
at data.network.util.NullOnEmptyConverterFactoryKt$nullOnEmptyConverterFactory$1$responseBodyConverter$1.convert(NullOnEmptyConverterFactory.kt:22)
at data.network.util.NullOnEmptyConverterFactoryKt$nullOnEmptyConverterFactory$1$responseBodyConverter$1.convert(NullOnEmptyConverterFactory.kt:17)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121)
at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Found out what caused this: none of the libraries mentioned above, Retrofit, GSon are at fault. If you follow the stack trace you'll see that it points to a problem, that the response does not conform to the gzip file format.
The OkHttp library expects the response to have a 10 byte header, if you're using gzip file format. If the response does not have it, the lib throws an exception.
The API is at fault in this case.
The ticket that helped me figure out the isse.
I know there are some other topics on this problem but I couldn't find the solution for my error so if you have any suggestions, please let me know. I'm trying to create a POST request with a param in the body to retrieve some data from the API.
My DataApi interface:
interface ChartsDataApi {
#POST(NetworkUtils.CASH_COLLECTION_URL)
fun getCashCollection(#Body guid: String) : Call<List<CashCollection>>
#POST(NetworkUtils.TICKETS_DETAILS_URL)
fun getTicketsDetails() : Call<List<TicketDetails>>
}
My fun where I'm using Retrofit and where I'm trying to get the response:
private fun geCashCollectionDetails(){
var params : MutableMap<String, String> = HashMap()
val BASE_URL = " here i wrote the base URL "
val gson = GsonBuilder()
.setLenient()
.create()
val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(ChartsDataApi::class.java)
GlobalScope.launch(Dispatchers.IO){
val response = api.getCashCollection(" **here i wrote the guid** ").awaitResponse()
if(response.isSuccessful){
val day = response.body()!!.lastIndex
Log.d("RAZZ", day.toString())
withContext(Dispatchers.Main){
}
}
}
My Data Class for CashCollection:
data class CashCollection(
#SerializedName("label")
val label: String?,
#SerializedName("value")
val value: String?)
Response from the server:
{"data":[{"label":"12.10.2020","value":"0,00"},{"label":"16.10.2020","value":"0,00"},{"label":"17.10.2020","value":"0,00"},{"label":"18.10.2020","value":"0,00"}],"currency":"EUR"}
Process: com.eschbachit.citylinemobile, PID: 32529
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:776)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:174)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:776)
When I'm trying to test and check the data I'm receiving, I get that error.
Please provide your data classes and response from server. This error means data from server can't be mapped into data classes that you provide as result of those requests.
Ok, data class must exactly reflect the structure of the response. Your CashCollection class must be
data class CashCollection(
val data: ArrayList<CashCollectionItem>,
val currency: String
) {
data class CashCollectionItem(
#SerializedName("label")
val label: String?,
#SerializedName("value")
val value: String?)
}
and type of response Call<CashCollection>
It appears you have incorrectly mapped your api response to kotlin classes. In your Retrofit api you say you expect the api to return a list of CashCollections ([{"label":"12.12.1212", "value": ""0,00}]), but the server returns a json object where that array is in the data property: {"data":[{"label":"12.12.1212", "value": "0,00"}]}
You should update your retrofit interface to correspond to what the api returns:
class CashCollectionResponse(
#SerializedName("data") data: List<CashCollection>
)
interface ChartsDataApi {
#POST("GetTheData")
fun getCashCollection(#Body guid: String): Call<CashCollectionResponse>
}