Firebase Cloud Functions Change Timeout - java

I'm using Firebase Cloud Functions library on Android, and using getHttpsCallable to call a cloud function.
The problem is that the function needs 10-15 seconds to return the result back to the client, so the client throws an exception java.net.SocketTimeoutException: timeout.
Code
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("info", info);
mFunctions.getHttpsCallable(function)
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
if (task.isSuccessful()) {
String result = (String) task.getResult().getData();
Log.v(Constants.LOG_TAG, result);
return result;
} else {
// The condition never was true, always logs the exception.
Exception e = task.getException();
Log.e(Constants.LOG_TAG, "Failed to join multiplayer room.", e);
return null;
}
}
});
How can I change the timeout so the client would wait more before throwing the exception?
Note. I'm not using OkHttp, Retrofit or the default system Networking functions, I'm using Firebase Cloud Functions library (getHttpsCallable) to call the function.

firebase-functions version 16.3.0, released 15 Mar 2019, adds the ability to configure the timeout.

I had same issue, so I called https functions with OkHttp instead of getHttpsCallable as a workaround.
The protocol of https.onCall is public.
https://firebase.google.com/docs/functions/callable
The code of calling https functions with OkHttp is here.
https://github.com/ryuta46/firebase-callable-okhttp/blob/56adc5e29a35bdb3b355c14d734e6145da4b6809/android/app/src/main/java/com/ttechsoft/okhttp_callable/MainActivity.kt#L184-L239
Editied.
The code of essential part is below.
private fun callWithOkHttp(functionName: String) {
val idToken = idToken ?: return
val instanceId = instanceId ?: return
val projectId = FirebaseApp.getInstance()?.options?.projectId ?: return
val url = "https://us-central1-$projectId.cloudfunctions.net/$functionName"
val jsonData = JSONObject()
jsonData.put("text", "inputText")
val json = JSONObject()
json.put("data", jsonData)
val requestBody = RequestBody.create(JSON, json.toString())
val request = Request.Builder()
.url(url)
.post(requestBody)
.addHeader("Authorization", "Bearer $idToken")
.addHeader("Firebase-Instance-ID-Token", instanceId)
.build()
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(1 , TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.build()
Log.i(TAG, "Start Okhttp")
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
val message = response.body()?.string() ?: "Network Error"
runOnUiThread {
textOkHttpResult.text = message
}
return
}
runOnUiThread {
textOkHttpResult.text = "OK"
}
val responseBody = response.body()
Log.i(TAG, responseBody?.string())
}
override fun onFailure(call: Call, e: IOException) {
val message = e.message ?: "Unknown Network error"
runOnUiThread {
textOkHttpResult.text = message
}
}
})
}

Related

Android retrofit async api calls won't work after adding okHttp interceptor to check for network connection

We have both asynchronous and synchronous calls implemented using retrofit and Either to map success/error. After adding the network interceptor asynchronous calls are returning bad responses(works fine on postman). I have tried adding a general error JSON response thinking Either is not able to catch the exceptions but still no luck. please suggest a fix or new approach
Interceptorclass -
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
try {
val response = chain.proceed(request)
val bodyString = response.body!!.string()
return response.newBuilder()
.body(ResponseBody.create(response.body?.contentType(), bodyString))
.build()
} catch (e: Exception) {
e.printStackTrace()
var msg = ""
when (e) {
is SocketTimeoutException -> {
msg = "Timeout - Please check your internet connection"
}
is UnknownHostException -> {
msg = "Unable to make a connection. Please check your internet"
}
is ConnectionShutdownException -> {
msg = "Connection shutdown. Please check your internet"
}
is IOException -> {
msg = "Server is unreachable, please try again later."
}
is IllegalStateException -> {
msg = "${e.message}"
}
else -> {
msg = "${e.message}"
}
}
return Response.Builder()
.request(request)
.protocol(Protocol.HTTP_1_1)
.code(999)
.message(msg)
.body(ResponseBody.create(null, "{${e}}")).build()
}
}`
client - val client1 = OkHttpClient.Builder() .addInterceptor(Interceptor) .build()
ServiceConfig.kt - Adding client as below
#Singleton
#Provides
#BaseUrl(BaseUrlType.SERVICES)
fun provideSupportRetrofit(
jsonConverters: Converter.Factory,
#HttpClient(ClientType.OAUTH) client: Call.Factory
): ServicesFactory {
return fakeServicesFactory ?: Retrofit.Builder()
.callFactory(client)
.baseUrl(baseUrlServices)
.addCallAdapterFactory(EitherCallAdapterFactory())
.addConverterFactory(EitherConverterFactory())
.addConverterFactory(FiberErrorConverterFactory())
.addConverterFactory(jsonConverters)
.addConverterFactory(primitiveTypeConverters)
.client(client1)
.build()
.asFactory
}
EitherCovertor.kt -
`override fun enqueue(callback: Callback<Either<*, *>>) {
call.enqueue(object : Callback<Either<*, *>> {
override fun onResponse(call: Call<Either<*, *>>, response: Response<Either<*, *>>) {
callback.onResponse(this#EitherCall, response.asEither)
}
}
override fun onFailure(call: Call<Either<*, *>>, t: Throwable) {
when (t) {
is Error -> {
Timber.e("Failure Error from API")
callback.onFailure(call, t)
}
else -> callback.onResponse(this#EitherCall, t.asEither)
}
}
})
}`
To get the error text in the header section
headers["Accept"] = "application/json"
and process the error in json form, this way worked for me
Response.ErrorListener { error: VolleyError? ->
if (error is TimeoutError || error is NoConnectionError) {
Constants.checkInternet(context)
} else if (error is ServerError) {
val responseBody = String(error.networkResponse.data, Charsets.UTF_8)
val errorMsg: String =
JSONObject(responseBody).getJSONObject(TAG_META).getJSONObject(
TAG_STATUS)
.getString(TAG_MESSAGE)
Toast.makeText(context, errorMsg + "", Toast.LENGTH_SHORT)
.show()
Log.e("responseBody", responseBody)
Log.e("errorMsg", errorMsg)
}
Loading.hide(loading)
}

Retrofit Post on Android kotlin not working but working on postman

I am trying to Hit the API using the Retrofit Post method to put the form data but the problem is that the API returns null all the time even if I try to pass the params. I tested the API by running it in Postman it works fine and I also added internet permission and set usesCleartextTraffic to true under manifest.xml, so it would be really helpful if anyone guides me to rectify this issue.
Output When I tried to debugg
call = Unresolved reference: call
response = Unresolved reference: response
this = {MainActivity#9661}
params = {HashMap#10022} size = 5
"password" -> "Ram#123"
"countryCode" -> {Integer#10042} 91
"name" -> "Ranjith"
"mobile" -> {Long#10046} 99********
"emailID" -> "var***#gmail.com"
Response back from the API with body null all the time
response = {Response#10220} "Response{protocol=http/1.1, code=400, message=Bad Request, url=http://*.***.***.***:***0/api/v1/****/auth/register}"
body = null
errorBody = {ResponseBody$1#10224}
content = {Buffer#10228} "[text={"code":400,"error":true,"msg":"Enter a valid name"}]"
contentLength = 52
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
uploadBt.setOnClickListener {
callApi()
}
}
private fun callApi() {
val params = HashMap<String?, Any?>()
params["name"] = "Ranjith"
params["mobile"] = 99*******
params["emailID"] = "var***#gmail.com"
params["password"] = "Ram#123"
params["countryCode"] = 91
RetrofitClient.instance.registerUser(params)
.enqueue(object : Callback<DefaultResponse>{
override fun onResponse(call: Call<DefaultResponse>, response: Response<DefaultResponse>) {
print(response)
if(response.isSuccessful)
{
Toast.makeText(applicationContext, response.body()?.msg, Toast.LENGTH_SHORT).show()
}else
{
var jsonObject: JSONObject? = null
try {
jsonObject = JSONObject(response.errorBody()!!.string())
val code = jsonObject.getString("code")
val error = jsonObject.getString("error")
val message = jsonObject.getString("msg")
Toast.makeText(applicationContext, message+"\n"+code+"\n"+error, Toast.LENGTH_LONG).show()
} catch (e: JSONException) {
Toast.makeText(applicationContext, e.toString(), Toast.LENGTH_LONG).show()
e.printStackTrace()
}
}
}
override fun onFailure(call: Call<DefaultResponse>, t: Throwable) {
Toast.makeText(applicationContext, t.toString(), Toast.LENGTH_SHORT).show()
}
})
}
}
Interface Api
interface Api {
#FormUrlEncoded
#POST("/api/v1/******/****/register")
fun registerUser(
#FieldMap params: HashMap<String?, Any?>
): Call<DefaultResponse>
}
RetrofitClient.kt object class
object RetrofitClient {
private const val BASE_URL = "http://3.1**.**5.1*1:****"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.method(original.method(), original.body())
val request = requestBuilder.build()
chain.proceed(request)
}.build()
val instance: Api by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(Api::class.java)
}
}
DefaultResponse.kt model data class
data class DefaultResponse(
#SerializedName("code")
val code: Int,
#SerializedName("err")
val error: Boolean,
#SerializedName("msg")
val msg: String
)
Through Postman

How to Read error body in Retrofit when response is not successful?

I am using retrofit in Android for api execution.
Sample Snippet
Call<UniversalPojo> call = apiInterface.storeData(AppClass.getInstance().getLoggedInUser().getRemember_token(), requestBody);
call.enqueue(new Callback<UniversalPojo>() {
#Override
public void onResponse(Call<UniversalPojo> call, Response<UniversalPojo> response) {
if (response.isSuccessful()) {
} else {
//I want to read code at this stage in string.
}
}
#Override
public void onFailure(Call<UniversalPojo> call, Throwable t) {
t.printStackTrace();
}
});
My question here is how to obtain the error in String at else block of if (response.isSuccessful()).
use OkHttpClient class's addInterceptor(interceptor: Interceptor) function
override the intercept(chain: Interceptor.Chain) function and throw exceptions as you expected:
class NetInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val code = response.code
val body = response.body
// if body is null or something unexpected
throw IOException("receive empty body")
// else do nothing
}
}

does retrofit interceptors proceed(request) make real request?

fun getTimeOutInterceptor(): Interceptor {
return Interceptor {
val request: Request = it.request()
val response = it.proceed(request)
try {
val content: String? = response.body()?.string()
response.newBuilder().body(ResponseBody.create(response.body()?.contentType(), content)).build()
} catch (exception: IOException) {
// Toast.makeText( BaseActivity.baseContext , "Time Out :)" , Toast.LENGTH_LONG).show()
Log.d("RetrofitClientInstance", "TimeOutFRomout")
}
response
}
}
1 .I do not understand the following
proceed(request) -> does not call HTTP server ,and send request
response.newBuilder().body(ResponseBody.create(response.body()?.contentType(), content)).build() -> why he rebuild
2 does multiple proceed(request) make it slow
3 how this will handle timeout

Integrate Google Photos Library API in android application

I want to integrate google photos api in android,I have done google sign in process but I am not able to get access token because i was getting error in FixedCredentialsProvider.create method while passing parameter.
PhotosLibrarySettings settings =
PhotosLibrarySettings.newBuilder()
.setCredentialsProvider(
FixedCredentialsProvider.create(/ Add credentials here. /))
.build();
try (PhotosLibraryClient photosLibraryClient =
PhotosLibraryClient.initialize(settings)) {
// Create a new Album with at title
Album createdAlbum = photosLibraryClient.createAlbum("My Album");
// Get some properties from the album, such as its ID and product URL
String id = album.getId();
String url = album.getProductUrl();
} catch (ApiException e) {
// Error during album creation
}
I was able to get a solutions to the issue
UserCredentials.newBuilder()
.setClientId("your client id")
.setClientSecret("your client secret")
.setAccessToken("Access Token")
.build()
You can pass this UserCredentials Object to ` FixedCredentialsProvider.create())
Get access token using
val client = OkHttpClient()
val requestBody = FormEncodingBuilder()
.add("grant_type", "authorization_code")
.add("client_id", "")
.add("client_secret", "")
.add("redirect_uri", "")
.add("code", "yourweb server id)
.build()
val request = Request.Builder()
.url("https://www.googleapis.com/oauth2/v4/token")
.post(requestBody)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(request: Request, e: IOException) {
}
#Throws(IOException::class)
override fun onResponse(response: Response) {
try {
val jsonObject = JSONObject(response.body().string())
mTokenExpired = SystemClock.elapsedRealtime() + jsonObject.optLong("expires_in") * 1000
accessTokenObservable.postValue(Resource.success("",jsonObject.optString("access_token")))
} catch (e: JSONException) {
e.printStackTrace()
}
}
})
https://github.com/erickogi/AndroidGooglePhotosApi
Hope it helps

Categories

Resources