I'm building an app on iOS and Android using AWS Amplify. I have a lambda expression that takes a longitude, a latitude, a username, an object id and returns me a boolean that tells me if yes or no the user is physically close to this object.
It works perfectly on my iOS app, I get true or false depending on the user location but on Android I always get null as a response.
Here's my function that triggers my lambda:
fun locationLambda() {
if ( ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) {
fusedLocationClient.lastLocation.addOnSuccessListener { location : Location? ->
if (location != null) {
val latitude = location.latitude.toString()
val longitude = location.longitude.toString()
val username = AWSMobileClient.getInstance().username
val objectId = (intent.extras.getSerializable("object") as Object).getId()
Log.i("ObjectId", objectId)
Log.i("Longitude", longitude)
Log.i("Latitude", latitude)
Log.i("Username", username)
mAWSAppSyncClient?.query(IsObjectFoundQuery.builder()
.latitude(latitude)
.longitude(longitude)
.username(username)
.objectID(objectId)
.build())
?.responseFetcher(AppSyncResponseFetchers.NETWORK_ONLY)
?.enqueue(locationCallback)
}
}
}
}
When I print in logs the parameters sent to the lambda everything is normal, the right values are sent.
And here's my callback function:
private val locationCallback = object : GraphQLCall.Callback<IsObjectFoundQuery.Data>() {
override fun onResponse( response: com.apollographql.apollo.api.Response<IsObjectFoundQuery.Data>) {
Log.i("Results", response.data()?.isObjectFound.toString())
}
override fun onFailure(e: ApolloException) {
Log.e("ERROR", e.toString())
}
}
Here I successfully get a response but response.data()?.isObjectFound is null
Am I doing something wrong here or do I have to look somewhere else?
Troubleshooting your lambda function without logging is a bit like finding something in the dark: Its very difficult. You are going to need logging for the client side and the server side (aka backend) to be able to troubleshoot this.
For the backend logging, AWS provides Cloudwatch logs for the Lambda and API Gateway.
So to check your Cloudwatch logs for your lambda, go into your AWS console for the Region where your Lambda is installed and go into the Cloudwatch service. Then select Logs on the side. You should see a log group named for your Lambda function. Clicking on this will show you the diagnostic information collected for your lambda when it is called (see image below for an example). Expand and collapse the log line entries.
Once you confirm you have cloudwatch logs turned on, follow this strategy:
Run a test call from the IOS client and confirm the output is successful on the client. Then check the cloudwatch logs (for both the API gateway and the Lambda function) of your IOS test and note what a success looks like.
Next run a second test from Android using your code above. Look in cloudwatch for the differences between the two and this should reveal details to help you troubleshoot what went wrong in your Android call.
If Amplify did not create Cloudwatch logs for your API gateway or Lambda function, follow the steps here in setting them up. Hope this helps you find the solution.
Related
eventHandler = object : IRtcEngineEventHandler() {
override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
Log.d("VideoCall1", "channel:$channel,uid:$uid,elapsed:$elapsed")
openScreen(ScreenType.VIDEO_CALL_SCREEN)
}
override fun onUserJoined(uid: Int, elapsed: Int) {
Log.d("VideoCall1", "onUserJoined:$uid")
viewModel.onUserJoined(uid)
}
override fun onUserOffline(uid: Int, reason: Int) {
Log.d("VideoCall1", "onUserOffline:$uid")
viewModel.onUserOffline(uid)
}
}
rtcEngine = RtcEngine.create(this, AppConstants.AGORA_APP_ID, eventHandler)
rtcEngine.apply {
enableVideo()
setClientRole(0)
// setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION)
joinChannel(token, channelName, "", userId.toInt())
}
Mobile user able to see web user but web user not able to see mobile user. Also I am getting callback of onUserJoined when web user joined channel.
I am using io.agora.rtc:full-sdk:3.5.0 and it works with
https://webdemo.agora.io/agora-web-showcase/examples/Agora-Web-Tutorial-1to1-Web/
Anyone have a suggestion or idea ?
Okay, so this took me hours of try and error to figure out a solution. I believe it's a bug in AgoraRTC but I didn't dive deep to figure out what's happening. Anyway, the hack I found goes like this:
In the Agora RTM script on the Web side, use a string uid while logging in:
this.client.login({ uid, token }); // uid had to be a string in my case
However, it says in the Agora documentation that the types of uid must match on web and mobile, since you are using int on mobile you might need to use an int on the web too, however when I tried to use an int, the login method threw an invalid uid type error!
But then again, it only worked if when joining the channel I supply the AgoraRTC client join method a null uid:
// uid (last argument) must be null
client.join(options.appid, options.channel, options.token, null);
It also works if the uid in the join function is the same uid in the login function but parsed to a number, but it doesn't if both are strings. And it also doesn't work if both are numbers, the login method throws an invalid uid variable type error...
Hope this helps anyone facing the same issue since it's a very weird error with no answers found anywhere else on the internet.
I have been trying to integrate AppCheck with my Android app, but I can't seem to make a valid request.
As for test purposes, I have been using the following code:
Android Code
class Application : MultiDexApplication() {
override fun onCreate() {
FirebaseApp.initializeApp(this)
val appCheck = FirebaseAppCheck.getInstance()
if (BuildConfig.DEBUG) appCheck.installAppCheckProviderFactory(DebugAppCheckProviderFactory.getInstance(), true)
else appCheck.installAppCheckProviderFactory(SafetyNetAppCheckProviderFactory.getInstance(), true)
super.onCreate()
}
}
class TestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
val data = "This is a test!"
Firebase.functions.getHttpsCallable("test").call(data).addOnCompleteListener {
if (it.isSuccessful) {
val result = it.result.data
print(result)
}
else {
val exception = it.exception
print(exception)
}
}
}
}
Function Code
const functions = require("firebase-functions")
exports.test = functions.https.onCall((data, context) => {
if (context.app == undefined) {
functions.logger.error("Test - Failed validating device integrity!")
throw new functions.https.HttpsError("failed-precondition", "The function must be called from an App Check verified app")
}
return data
})
I have added the DebugAppCheckProviderFactory token to the Firebase console, but no matter what I do, if it is an emulator or physical device, when I call the function, the failed-precondition exception is always thrown.
Checking the function logs, I can see that the app is missing:
I have already read the documentation multiple times and I can't seem to be missing any step. Am I missing something or is there anything I can do to find the root cause for this?
Thank you for your time!
EDIT:
As for Martin suggestion, I created a new OnRequest function and added the X-Firebase-AppCheck header. I received the token correctly and was able to validate it successfully with:
firebaseAdmin.appCheck().verifyToken(appCheckToken)
So, my guess is that Android is not adding the X-Firebase-AppCheck automatically to the OnCall function like it should.
I ran the code and made some breakpoints through the code and noticed the following. The call method from Firebase is adding the Firebase-Instance-ID-Token but I can't seem to find the X-Firebase-AppCheck header anywhere. Maybe I am not supposed to see this value, or maybe I just can't find where it is being added. Or maybe it is not added at all, thus I can't validate my context.app at all.
It may be required to obtain an AppCheckToken:
FirebaseAppCheck
.getInstance()
.getAppCheckToken(false)
.addOnSuccessListener(new OnSuccessListener<AppCheckToken>() {
#Override
public void onSuccess(#NonNull AppCheckToken tokenResponse) {
String appCheckToken = tokenResponse.getToken();
...
}
});
Which has to be passed along, with the HTTP request (the example shows Retrofit, which could also be used instead of HttpsCallable:
#GET("yourExampleEndpoint")
Call<List<String>> exampleData(
#Header("X-Firebase-AppCheck") String appCheckToken,
...
);
Where FirebaseFunctions.getInstance().getHttpsCallable().call() doesn't tell how or if one has to explicitly set that X-Firebase-AppCheck header. Cloud Functions should usually receive an X-Firebase-AppCheck header - with the previously retrieved AppCheck token:
const appCheckToken = req.header('X-Firebase-AppCheck');
if (!appCheckToken) {
res.status(401);
return next('Unauthorized');
}
try {
const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken);
// If verifyToken() succeeds, continue with the next middleware function in the stack.
return next();
} catch (err) {
res.status(401);
return next('Unauthorized');
}
One can also issue own tokens ...and checking for context-app is also valid.
Update: The Protocol specification for https.onCall reads:
Optional: X-Firebase-AppCheck: <token>
The Firebase App Check token provided by the client app making the request. The backend automatically verifies this token and decodes it, injecting the appId in the handler's context. If the token cannot be verified, the request is rejected.Available for SDK >=3.14.0
To install the minimum required NodeJS dependencies:
npm install firebase-functions#">=3.14.0"
npm install firebase-admin#">=9.8.0"
And last, but not least ...there's even a debug helper for that:
dependencies {
debugImplementation "com.google.firebase:firebase-appcheck-debug:16.0.0-beta02"
}
After testing everything with no success, in a desperate move, I tried to look to unrelated files and found the solution.
In my build.gradle I was importing all the Firebase dependencies with the bom:
implementation platform("com.google.firebase:firebase-bom:27.0.0")
implementation "com.google.firebase:firebase-crashlytics-ktx"
implementation "com.google.firebase:firebase-analytics-ktx"
implementation "com.google.firebase:firebase-firestore-ktx"
implementation "com.google.firebase:firebase-functions-ktx"
implementation "com.google.firebase:firebase-auth-ktx"
So I thought: "Can the firebase-bom be importing an outdated functions-ktx dependency?"
And guess what? As soon as I imported the dependencies without the bom, I started to see the X-Firebase-AppCheck being added and the context.app being valid.
I ended up with the following build.gralde:
implementation "com.google.firebase:firebase-crashlytics-ktx:18.2.1"
implementation "com.google.firebase:firebase-analytics-ktx:19.0.0"
implementation "com.google.firebase:firebase-firestore-ktx:23.0.3"
implementation "com.google.firebase:firebase-functions-ktx:20.0.0"
implementation "com.google.firebase:firebase-auth-ktx:21.0.1"
After a little more investigation, I found out that I was using an outdated firebase-bom version. Android Studio used to let me know if there was a new version for the dependencies, but it did not notice me for the firebase-bom. Turns out, that I just need to update my firebase-bom dependency.
TL;DR
Check your Android Firebase libraries version!
According to GitHub sample project and Tealium's documentation for Kotlin I created such TealiumHelper:
object TealiumHelper {
fun init(application: Application) {
val tealiumConfig = TealiumConfig(
application,
accountName = BuildConfig.TEALIUM_ACCOUNT_NAME,
profileName = BuildConfig.TEALIUM_PROFILE_NAME,
environment = BuildConfig.TEALIUM_ENVIRONMENT
)
// Display logs only for DEV
Logger.Companion.logLevel = BuildConfig.TEALIUM_LOGLEVEL
// Make it start working
Tealium.create(BuildConfig.TEALIUM_INSTANCE, tealiumConfig)
}
fun trackEvent(name: String, data: Map<String, Any>? = null) {
val eventDispatch = TealiumEvent(name, data)
Tealium[BuildConfig.TEALIUM_INSTANCE]?.track(eventDispatch)
}
fun trackView(name: String, data: Map<String, Any>? = null) {
val viewDispatch = TealiumView(name, data)
Tealium[BuildConfig.TEALIUM_INSTANCE]?.track(viewDispatch)
}
}
I get logs by Tealium so it should be working fine.
2021-05-17 14:28:56.694 22812-22894/xxx.xxx.xxx D/Tealium-1.2.2: Dispatch(fc5c0) - Ready - {tealium_event_type=view, tealium_event=XXX ...}
But after I call trackView or trackEvent, my events don't go to server.
There is also additional log infor which I don't know what does it mean. Documentation doesn't say much about it:
2021-05-17 14:28:59.352 22812-22894/xxx.xxx.xxx I/Tealium-1.2.2: Asset not found (tealium-settings.json)
How could I fix it? What does Asset not found mean?
#deadfish I manage the mobile team here at Tealium, so I can point you in the right direction. You can safely ignore the Asset Not Found log - this just indicates that no local settings file was found, so by default, the remote settings will be used. We'll look at addressing this to make the log message a little more helpful. There are a couple of things that might cause data not to be sent. Firstly, I can't see any Dispatchers specified in your TealiumConfig object. As specified in the docs, you need to add either the TagManagement or Collect dispatcher, which are the modules that send the data from the device. The Collect module sends data to the server-side platform (I think this is what you need), while the TagManagement module processes the data client-side using JavaScript. Please feel free to get in touch with our support team or contact us by raising an issue on our GitHub repo if you need further personalized support.
I'm trying to generate a new messaging token but it seems to generate the same one, over and over again.
I tried both
FirebaseMessaging.getInstance().deleteToken().addOnCompleteListener {
Log.d("Notifications", "Token Deleted!!!")
}
and
FirebaseInstallations.getInstance().delete().addOnCompleteListener {
Log.d("Notifications", "Token Deleted!!!")
}
Neither seem to trigger the
override fun onNewToken(refreshedToken: String) {}
of the service. And each time i query for the current token i get the same one.
What am i doing wrong?
I am not exactly sure if you can delete the generated token to get a new one. However, as per documentation a token is generated once on app's initial startup.
If you want a new token - simply uninstall the app and install it once again. Then Firebase will generate a new token.
I was having the same issue when trying to log out users.
I would recommend using the depreciated method below:
Thread(Runnable {
Thread.sleep(1000)
FirebaseInstanceId.getInstance().deleteInstanceId()
}).start()
This will not call onNewToken, however, if you now call..
FirebaseMessaging.getInstance().token.addOnCompleteListener { -> task
// get the result here
}
Result should be a new token
I am trying out odata4j in my android app to retrieve data from a DB that can be accessed from a WCF service.
ODataConsumer co = ODataConsumer.create("http://xxx.xx.xx.xxx:xxxx/Users");
for(OEntity user : co.getEntities("Users").execute())
{
// do stuff
}
However this crashes at the call to getEntities. I have tried a variety of other calls as well, such as
Enumerable<OEntity> eo = co.getEntities("Users").execute();
OEntity users = eo.elementAt(0);
However this also crashes at eo.elementAt(0).
The logcat doesn't tell me anything, and the callstack seems to be Suspended at ActivityThread.performLaunchActivity.
Entering "http://localhost:xxxx/Users" in my web browser on the other hand works as expected and returns the users in my DB in xml format.
Any ideas on how I can debug this?
To log all http requests/responses:
ODataConsumer.dump.all(true);
The uri passed to the consumer .create call should be the service root. e.g. .create("http://xxx.xx.xx.xxx:xxxx/"); Otherwise your code looks fine.
Note the Enumerable behaves like the .net type - enumeration is deferred until access. If you plan on indexing multiple times into the results, I'd suggest you call .toList() first.
Let me know what you find out.
Hope that helps,
- john
I guess the call should be:
ODataConsumer co = ODataConsumer.create("http://xxx.xx.xx.xxx:xxxx");
for(OEntity user : co.getEntities("Users").execute())
{
// do stuff
}
create defines service you want to connect but Users is the resource you want to query.
Can you try this way.
OEntity oEntity;
OQueryRequest<OEntity> oQueryRequest= oDataJerseyConsumer.getEntities(entityName);
List<OEntity> list= oQueryRequest.execute().toList();
for (OEntity o : list) {
List<OProperty<?>> props = o.getProperties();
for (OProperty<?> prop : props) {
System.out.println(prop.getValue().toString());
}
}