Repository navigation
feat(fcm): Migrate topic management to FCM v1 REST API - #546
Conversation
Migrates topic subscription and unsubscription from the deprecated Instance ID (IID) service to the FCM v1 REST API. The new implementation calls the FCM v1 topicSubscriptions endpoints for each registration token concurrently (bounded by a semaphore) and handles idempotent subscription scenarios (HTTP 409 / ALREADY_EXISTS). Deprecates the legacy IID topic methods. Testing: Added unit tests in `FirebaseAdmin.Tests/Messaging/FirebaseMessagingClientTest.cs` and updated `FirebaseAdmin.Tests/Messaging/FirebaseMessagingTest.cs` to verify FCM v1 topic management requests, error mappings, argument validations, and legacy endpoints. All unit tests passed across target frameworks. Context Sources Used: - id: firebase-admin-dotnet
de98041 to
1c9ee0a
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces new non-legacy SubscribeToTopicAsync and UnsubscribeFromTopicAsync methods to FirebaseMessaging and FirebaseMessagingClient that utilize the new v1 FCM API, while deprecating the old Instance ID API-based methods. It also adds validation, concurrent request handling using SemaphoreSlim, updated error mapping, and comprehensive unit tests. The review feedback highlights three key areas for improvement: explicitly catching and rethrowing OperationCanceledException to prevent swallowing cancellations in the generic catch block, properly disposing of the SemaphoreSlim instance to avoid resource leaks, and using RegexOptions.Compiled on the static topic name Regex to optimize performance.
|
I feel like our project really needs this. Our setup is: One backend, one service account, and two Firebase projects. Sends and registrations work just fine. Our wrapper calls I'd rather not work around it:
If there's a rough release target date, or anything else relevant to it, please do let me know. |
|
For anyone who lands here with the same setup (one service account used for several Firebase projects): sends work, but every
I've run tests locally with the changes in here and a real service account my API would use. Until it's released, you can build it yourself. This branch has the PR changes plus the bot's review I'm not so sure about the release schedule you have, but hopefully we all get it soon. Thanks for the fix! |
|
Hi @vinclou, thank you for your comments and for confirming that these changes address your needs. We are currently waiting on the remaining reviews to merge this into the main branch. Once merged, we intend to include this in the next SDK release cycle. This is a high-priority change for us, so rest assured we are actively working to move this forward. |
Addresses review feedback on PR #546 by: - Explicitly catching and rethrowing OperationCanceledException in topic management to ensure cancellation propagates properly rather than converting to UNKNOWN_ERROR. - Disposing SemaphoreSlim inside a using block in SendTopicManagementRequestAsync to prevent resource leaks. - Specifying RegexOptions.Compiled on the static TopicNamePattern regex to improve performance. Testing: Added TopicManagement_Cancellation unit tests in FirebaseMessagingClientTest.cs to verify cancellation propagation and verified that all 1,088 unit tests pass. Context Sources Used: - id: firebase-admin-dotnet
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request migrates topic subscription and unsubscription functionality in FirebaseMessaging from the legacy Instance ID API to the modern FCM v1 REST API, processing requests concurrently using a semaphore. It also marks the legacy methods as obsolete, updates error mapping in ErrorInfo, and adds comprehensive unit tests. The feedback suggests defining a constant for the maximum concurrent topic requests instead of using a magic number, and optimizing the subscription request body by using a static StringContent with "{}" to avoid the overhead of serializing an empty anonymous object.
| private const string FcmSendUrl = FcmBaseUrl + "/v1/projects/{0}/messages:send"; | ||
| private const string FcmBatchUrl = FcmBaseUrl + "/batch"; | ||
|
|
||
| private static readonly System.Text.RegularExpressions.Regex TopicNamePattern = |
There was a problem hiding this comment.
| var cleanTopic = topic.StartsWith("/topics/") ? topic.Substring("/topics/".Length) : topic; | ||
| var encodedTopic = Uri.EscapeDataString(cleanTopic); | ||
|
|
||
| using (var semaphore = new SemaphoreSlim(Math.Min(registrationTokens.Count, 100))) |
| { | ||
| Method = HttpMethod.Post, | ||
| RequestUri = new Uri(url), | ||
| Content = NewtonsoftJsonSerializer.Instance.CreateJsonHttpContent(new { }), |
There was a problem hiding this comment.
Avoid the overhead of allocating an anonymous object and serializing it to JSON via reflection for every single token subscription request. Since the body is always an empty JSON object ({}), you can use a static StringContent directly.
Content = new StringContent("{}", System.Text.Encoding.UTF8, "application/json"),
jonathanedey
left a comment
There was a problem hiding this comment.
LGTM with gemini review comments!
…e subscription payload Addresses latest PR review feedback on PR #546 by: - Defining the `MaxConcurrentTopicRequests` constant (set to 100) instead of using a magic number for the semaphore capacity. - Replacing reflection-based serialization of empty anonymous objects with `StringContent("{}", Encoding.UTF8, "application/json")` for subscription request bodies to reduce allocation and CPU overhead. Testing: Ran the unit test suite (`dotnet test FirebaseAdmin/FirebaseAdmin.Tests --framework net6.0`) with all 1,088 tests passing, and verified code formatting with `dotnet format`. Context Sources Used: - id: firebase-admin-dotnet
…on tests
With the migration of topic management to the FCM v1 REST API (`/v1/projects/{projectId}/registrations/{token}/topicSubscriptions`), non-existent or foreign tokens like `"token1"` and `"token2"` return HTTP 403 PERMISSION_DENIED (`"permission-denied"`) from the live FCM backend rather than the legacy Instance ID API's INVALID_ARGUMENT (`"invalid-argument"`). Updated `SubscribeToTopic` and `UnsubscribeFromTopic` integration test assertions to expect `"permission-denied"`.
Testing: Built `FirebaseAdmin.IntegrationTests` across `net462` and `net6.0` with 0 warnings/errors, formatted code with `dotnet format`, and ran unit tests with all 1,088 tests passing.
Context Sources Used:
- id: firebase-admin-dotnet
Migrates topic subscription and management from the deprecated Instance ID (IID) service to the FCM v1 REST API. The new implementation calls the
FCM v1 topicSubscriptionsendpoints for each registration token concurrently (bounded by a semaphore) and handles idempotent subscription scenarios (HTTP 409 / ALREADY_EXISTS). Deprecates the legacy IID topic methods.