Closed mman closed 1 month ago
Initial investigation reveals that dictionary that is passed to firebase-admin
sdk does not strictly respect the required data format.
This is what we get after preparing a dict to be sent to FCM:
{
"data": {
"android": {
"priority": "high",
"data": {
"alert": "Something",
"title": "Title",
"category": "default",
"threadId": "219cbd3423774dc1a407406e06bbc3a1",
"cameraID": "219cbd3423774dc1a407406e06bbc3a1",
"cameraName": "iPhone 15 Pro",
"interruptionLevel": "time-sensitive"
},
"ttl": 35999
},
"tokens": [
"f6d_Lyr6TH6OZhFLRMFDfs:APA91bH-ywNh186FRsx7XmzBKIjBZq4rFiKRTcCdiPXtiFmhF6oMKr-Lwwytise4TV0kAU_s4XVB6JpGShyga92oMUlr98mU5CcPYIrarTE2BJdHIaWBH6Rqff4exx50rzQGjwySZpfR"
]
},
"push_id": "Np3jwjmqHk",
"time": "2024-04-22T18:31:58.403Z"
}
Notice how push_id
and time
parameters are actually present at top level, where they make no sense at all, since FCM does not require/expect them at all (https://firebase.google.com/docs/reference/admin/node/firebase-admin.messaging.basemessage.md#basemessage_interface)
The offending code comes from here where the dict is constructed:
The push_id
and time
keys should be pushed out to data if I understand things correctly.
@jimnor0xF would you mind taking a look at this? It may be easy for you to fix since you recently worked on some related changes 🙂
Couple more interesting points. If we start from the end, which is the ParseFirebaseMessagingService
handling incoming push delivery within the Android app, it actually expects to receive a message with three parameters:
{
push_id: "randomString",
time: "time in ISO format"
data: "JSON encoded dictionary of anything useful..."
}
The code praising the incoming message has not changed for ages and is basically here:
See also my debugging screenshot when using legacy push delivery and fresh build of Android app:
Only when all of these three parameters of the incoming message are not null
will the ParsePush handling do anything useful on the Android client side.
If I understand everything correctly, then legacy GCM.js
would take whatever payload it gets, will only encode it to String, decorate it with push_id
and time
and send it out.
The FCM.js
has two problems at the moment.
push_id
and time
in wrong hierarchy location, so these (mandatory for Parse Android SKD) just get ignored.data
dictionary so that it actually gets received decoded in the Parse Android SDK which breaks the receiving logic.I am not sure yet what is the proper fix here.
Workaround may be to supply rawData
with prepared android payload when sending a message, thus APN push sender will use existing working mechanism, and FCM sender will pick up raw data without any conversion.
I have crafted an ugly PR which shows that data
need to be JSON encoded to String and decorated with push_id
and time
in order to be successfully delivered to Parse Android SDK. I have tested it with my app and it seems to work without changing any Parse.Push.send
code, however I have not tested it in any other way. I am using the FCM.js
only for delivery of push notifications to Android.
@mman Nice find! Must have missed this since we do not use the Parse SDK on the client side for push notifications, only firebase_messaging. I'll have a look at your PR.
...on a quick side note, were you able to test this on ios as well? Does the Apple Parse SDK expect push_id etc to be present?
@jimnor0xF Nope, I have not tested this with iOS. I send notifications to iOS directly via APNS.js
, using Firebase only for Android.
...on a quick side note, were you able to test this on ios as well? Does the Apple Parse SDK expect push_id etc to be present?
I do not think they are used/required by Apple or Parse iOS SDK. They seem to be only an implementation detail in that they are set on the Parse Server, then delivered to Parse Android SDK, and there used to queue/buffer incoming push notifications based on their push_id
and time
.
Looks like it may have been used to remove duplicates and keep only couple recent pushes queued?
Just to confirm that I have pushed 6.2.0 to production and I can send push notifications to Android users without changes in the cloud code and without changes on the client side. Simply modifying the push adapter configuration by adding firebaseServiceAccount
is enough. Thanks!
I've done some testing as well and can confirm @mman's result. I have only tested with Android, not with Apple. It may be possible that there are edge cases in which this doesn't work, but for the moment I'm unaware of any.
I've overhauled the README for better structure and to provide better guidance in light of the FCM legacy API deprecation. Everything seems to be ready, so if there are no contrary opinions we'll prepare to send out a tweet in the next days.
Are there any changes we should make in the Android SDK regarding push_id
and push_time
? If so, could someone open an issue there?
No changes on the Android SDK side as far as I could see
From the comments above regarding push_id
and push_time
I assumed they were legacy params that should be removed, but we cannot remove them because the Parse Android SDK expects them. Or did i misread?
We would need to look into history to figure out why they were added in the first place and what was their purpose. From my short look I could see that push_id
meant to categorise incoming notifications into groups using time
to sort them and than pass them to BroadcastReceiver where you anyway need to build a notification and display it and track of what is onscreen? So perhaps they can be removed and just push what is received into a broadcast receiver, but it will need more time to investigate. I will not have a time to look into it in the near future I am afraid...
Thanks for explaining, I guess we can leave as is for now. Opened https://github.com/parse-community/parse-server/issues/9126.
New Issue Checklist
Issue Description
I have an existing code that is sending push notifications to both Android and iOS via the same call. The invocation in the cloud code looks like this:
where
payload
has been set to:You can see that it uses default iOS
alert
dictionary with couple of standard iOS params (interruptionLevel
,mutable-content
, ...) as well as user defined params (for exampleurl
).Updating to the latest
parse-server-push-adapter@6
and addingfirebaseServiceAccount
to the Android configuration does not work out of the box:Actual Outcome
Error 1: Error: android.data must only contain string values
Using the existing code and just reconfiguring
android
push adapter config to usefirebaseServiceAccount
does not work.This indicates that the push notification was correctly targeted and posted to FCM API, however FCM API returned an error
android.data must only contain string values
. This is most probably coming from the fact that push data contain analert
dictionary withbody
andtitle
instead of using just flatalert
andtitle
.Flattening the payload makes the error go away, but:
Parse log output is:
Error 2: Notification does not show up on Android device
The client side SDK receives a
RemoteMessage
withBundle
containing all the keys passed tosendPush
but does not contain thedata
key, thus the code below returnsnull
for all important params and push notification, despite reaching the device just gets silently dropped inhandlePush
.https://github.com/parse-community/Parse-SDK-Android/blob/a08c5d1594b3a083671b493804eb1970c1ce6441/fcm/src/main/java/com/parse/fcm/ParseFirebaseMessagingService.java#L20-L42
Expected Outcome
I would expect existing code to either work or get some pointers how to adapt it to work using the new adapter.
Environment
Client
Server