Skip to content

Commit bc29211

Browse files
authored
Merge pull request #100 from firebase/hkj-topic-mgt
feat(fcm): Added the SubscribtToTopicAsync() and UnsubscribeFromTopicAsync() APIs
2 parents 37fa0ad + 8657e98 commit bc29211

File tree

7 files changed

+77
-31
lines changed

7 files changed

+77
-31
lines changed

FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseMessagingTest.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ public async Task SendMulticast()
125125
[Fact]
126126
public async Task SubscribeToTopic()
127127
{
128-
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync("test-topic", new List<string> { "token1", "token2" });
128+
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync(
129+
new List<string> { "token1", "token2" }, "test-topic");
129130
Assert.NotNull(response);
130131
Assert.Equal(2, response.FailureCount);
131132
Assert.Equal("invalid-argument", response.Errors[0].Reason);
@@ -137,7 +138,8 @@ public async Task SubscribeToTopic()
137138
[Fact]
138139
public async Task UnsubscribeFromTopic()
139140
{
140-
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync("test-topic", new List<string> { "token1", "token2" });
141+
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync(
142+
new List<string> { "token1", "token2" }, "test-topic");
141143
Assert.NotNull(response);
142144
Assert.Equal(2, response.FailureCount);
143145
Assert.Equal("invalid-argument", response.Errors[0].Reason);

FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseMessagingSnippets.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
using System;
1616
using System.Collections.Generic;
17-
using System.Linq;
1817
using System.Threading.Tasks;
1918
using FirebaseAdmin.Messaging;
2019

@@ -321,5 +320,47 @@ internal static Message CreateMultiPlatformsMessage()
321320
// [END multi_platforms_message]
322321
return message;
323322
}
323+
324+
internal static async Task SubscribeToTopicAsync(string topic)
325+
{
326+
// [START subscribe_to_topic]
327+
// These registration tokens come from the client FCM SDKs.
328+
var registrationTokens = new List<string>()
329+
{
330+
"YOUR_REGISTRATION_TOKEN_1",
331+
// ...
332+
"YOUR_REGISTRATION_TOKEN_n",
333+
};
334+
335+
// Subscribe the devices corresponding to the registration tokens to the
336+
// topic
337+
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync(
338+
registrationTokens, topic);
339+
// See the TopicManagementResponse reference documentation
340+
// for the contents of response.
341+
Console.WriteLine($"{response.SuccessCount} tokens were subscribed successfully");
342+
// [END subscribe_to_topic]
343+
}
344+
345+
internal static async Task UnsubscribeFromTopicAsync(string topic)
346+
{
347+
// [START unsubscribe_from_topic]
348+
// These registration tokens come from the client FCM SDKs.
349+
var registrationTokens = new List<string>()
350+
{
351+
"YOUR_REGISTRATION_TOKEN_1",
352+
// ...
353+
"YOUR_REGISTRATION_TOKEN_n",
354+
};
355+
356+
// Unsubscribe the devices corresponding to the registration tokens from the
357+
// topic
358+
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync(
359+
registrationTokens, topic);
360+
// See the TopicManagementResponse reference documentation
361+
// for the contents of response.
362+
Console.WriteLine($"{response.SuccessCount} tokens were unsubscribed successfully");
363+
// [END unsubscribe_from_topic]
364+
}
324365
}
325366
}

FirebaseAdmin/FirebaseAdmin.Tests/Messaging/FirebaseMessagingTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public async Task SubscribeWithClientFactory()
138138
Assert.NotNull(messaging);
139139
Assert.Same(messaging, FirebaseMessaging.GetMessaging(app));
140140

141-
var response = await messaging.SubscribeToTopicAsync("test-topic", new List<string> { "test-token" });
141+
var response = await messaging.SubscribeToTopicAsync(new List<string> { "test-token" }, "test-topic");
142142
Assert.Equal(0, response.FailureCount);
143143
Assert.Equal(1, response.SuccessCount);
144144
app.Delete();
@@ -163,7 +163,7 @@ public async Task UnsubscribeWithClientFactory()
163163
Assert.NotNull(messaging);
164164
Assert.Same(messaging, FirebaseMessaging.GetMessaging(app));
165165

166-
var response = await messaging.UnsubscribeFromTopicAsync("test-topic", new List<string> { "test-token" });
166+
var response = await messaging.UnsubscribeFromTopicAsync(new List<string> { "test-token" }, "test-topic");
167167
Assert.Equal(0, response.FailureCount);
168168
Assert.Equal(1, response.SuccessCount);
169169
app.Delete();

FirebaseAdmin/FirebaseAdmin.Tests/Messaging/InstanceIdClientTest.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public async Task SubscribeToTopicAsync()
4141

4242
var client = new InstanceIdClient(factory, MockCredential);
4343

44-
var result = await client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" });
44+
var result = await client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic");
4545

4646
Assert.Equal(1, result.SuccessCount);
4747
}
@@ -57,7 +57,7 @@ public async Task UnsubscribeFromTopicAsync()
5757

5858
var client = new InstanceIdClient(factory, MockCredential);
5959

60-
var result = await client.UnsubscribeFromTopicAsync("test-topic", new List<string> { "abc123" });
60+
var result = await client.UnsubscribeFromTopicAsync(new List<string> { "abc123" }, "test-topic");
6161

6262
Assert.Equal(1, result.SuccessCount);
6363
}
@@ -75,7 +75,7 @@ public async Task BadRequest()
7575
var client = new InstanceIdClient(factory, MockCredential);
7676

7777
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
78-
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
78+
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
7979

8080
Assert.Equal(ErrorCode.InvalidArgument, exception.ErrorCode);
8181
Assert.Equal("Unexpected HTTP response with status: 400 (BadRequest)\nBadRequest", exception.Message);
@@ -97,7 +97,7 @@ public async Task Unauthorized()
9797
var client = new InstanceIdClient(factory, MockCredential);
9898

9999
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
100-
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
100+
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
101101

102102
Assert.Equal(ErrorCode.Unauthenticated, exception.ErrorCode);
103103
Assert.Equal("Unexpected HTTP response with status: 401 (Unauthorized)\nUnauthorized", exception.Message);
@@ -119,7 +119,7 @@ public async Task Forbidden()
119119
var client = new InstanceIdClient(factory, MockCredential);
120120

121121
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
122-
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
122+
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
123123

124124
Assert.Equal(ErrorCode.PermissionDenied, exception.ErrorCode);
125125
Assert.Equal("Unexpected HTTP response with status: 403 (Forbidden)\nForbidden", exception.Message);
@@ -141,7 +141,7 @@ public async Task NotFound()
141141
var client = new InstanceIdClient(factory, MockCredential);
142142

143143
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
144-
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
144+
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
145145

146146
Assert.Equal(ErrorCode.NotFound, exception.ErrorCode);
147147
Assert.Equal("Unexpected HTTP response with status: 404 (NotFound)\nNotFound", exception.Message);
@@ -163,7 +163,7 @@ public async Task ServiceUnavailable()
163163
var client = new InstanceIdClient(factory, MockCredential);
164164

165165
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
166-
() => client.SubscribeToTopicAsync("test-topic", new List<string> { "abc123" }));
166+
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
167167

168168
Assert.Equal(ErrorCode.Unavailable, exception.ErrorCode);
169169
Assert.Equal("Unexpected HTTP response with status: 503 (ServiceUnavailable)\nServiceUnavailable", exception.Message);

FirebaseAdmin/FirebaseAdmin/Messaging/FirebaseMessaging.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,23 +315,25 @@ public async Task<BatchResponse> SendMulticastAsync(
315315
/// <summary>
316316
/// Subscribes a list of registration tokens to a topic.
317317
/// </summary>
318-
/// <param name="topic">The topic name to subscribe to. /topics/ will be prepended to the topic name provided if absent.</param>
319318
/// <param name="registrationTokens">A list of registration tokens to subscribe.</param>
319+
/// <param name="topic">The topic name to subscribe to. /topics/ will be prepended to the topic name provided if absent.</param>
320320
/// <returns>A task that completes with a <see cref="TopicManagementResponse"/>, giving details about the topic subscription operations.</returns>
321-
public async Task<TopicManagementResponse> SubscribeToTopicAsync(string topic, List<string> registrationTokens)
321+
public async Task<TopicManagementResponse> SubscribeToTopicAsync(
322+
IReadOnlyList<string> registrationTokens, string topic)
322323
{
323-
return await this.instanceIdClient.SubscribeToTopicAsync(topic, registrationTokens);
324+
return await this.instanceIdClient.SubscribeToTopicAsync(registrationTokens, topic);
324325
}
325326

326327
/// <summary>
327328
/// Unsubscribes a list of registration tokens from a topic.
328329
/// </summary>
329-
/// <param name="topic">The topic name to unsubscribe from. /topics/ will be prepended to the topic name provided if absent.</param>
330330
/// <param name="registrationTokens">A list of registration tokens to unsubscribe.</param>
331+
/// <param name="topic">The topic name to unsubscribe from. /topics/ will be prepended to the topic name provided if absent.</param>
331332
/// <returns>A task that completes with a <see cref="TopicManagementResponse"/>, giving details about the topic unsubscription operations.</returns>
332-
public async Task<TopicManagementResponse> UnsubscribeFromTopicAsync(string topic, List<string> registrationTokens)
333+
public async Task<TopicManagementResponse> UnsubscribeFromTopicAsync(
334+
IReadOnlyList<string> registrationTokens, string topic)
333335
{
334-
return await this.instanceIdClient.UnsubscribeFromTopicAsync(topic, registrationTokens);
336+
return await this.instanceIdClient.UnsubscribeFromTopicAsync(registrationTokens, topic);
335337
}
336338

337339
/// <summary>

FirebaseAdmin/FirebaseAdmin/Messaging/InstanceIdClient.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
using System;
1616
using System.Collections.Generic;
17-
using System.Linq;
1817
using System.Net.Http;
1918
using System.Threading.Tasks;
2019
using FirebaseAdmin.Util;
@@ -61,30 +60,30 @@ public InstanceIdClient(HttpClientFactory clientFactory, GoogleCredential creden
6160
/// <summary>
6261
/// Subscribes a list of registration tokens to a topic.
6362
/// </summary>
63+
/// <param name="registrationTokens">A list of registration tokens to subscribe.</param>
6464
/// <param name="topic">The topic name to subscribe to. /topics/ will be prepended to the
6565
/// topic name provided if absent.</param>
66-
/// <param name="registrationTokens">A list of registration tokens to subscribe.</param>
6766
/// <returns>A task that completes with a <see cref="TopicManagementResponse"/>, giving
6867
/// details about the topic subscription operations.</returns>
6968
public async Task<TopicManagementResponse> SubscribeToTopicAsync(
70-
string topic, List<string> registrationTokens)
69+
IReadOnlyList<string> registrationTokens, string topic)
7170
{
72-
return await this.SendInstanceIdRequest(topic, registrationTokens, IidSubscriberPath)
71+
return await this.SendInstanceIdRequest(registrationTokens, topic, IidSubscriberPath)
7372
.ConfigureAwait(false);
7473
}
7574

7675
/// <summary>
7776
/// Unsubscribes a list of registration tokens from a topic.
7877
/// </summary>
78+
/// <param name="registrationTokens">A list of registration tokens to unsubscribe.</param>
7979
/// <param name="topic">The topic name to unsubscribe from. /topics/ will be prepended to
8080
/// the topic name provided if absent.</param>
81-
/// <param name="registrationTokens">A list of registration tokens to unsubscribe.</param>
8281
/// <returns>A task that completes with a <see cref="TopicManagementResponse"/>, giving
8382
/// details about the topic unsubscription operations.</returns>
8483
public async Task<TopicManagementResponse> UnsubscribeFromTopicAsync(
85-
string topic, List<string> registrationTokens)
84+
IReadOnlyList<string> registrationTokens, string topic)
8685
{
87-
return await this.SendInstanceIdRequest(topic, registrationTokens, IidUnsubscribePath)
86+
return await this.SendInstanceIdRequest(registrationTokens, topic, IidUnsubscribePath)
8887
.ConfigureAwait(false);
8988
}
9089

@@ -97,7 +96,7 @@ public void Dispose()
9796
}
9897

9998
private async Task<TopicManagementResponse> SendInstanceIdRequest(
100-
string topic, List<string> registrationTokens, string path)
99+
IReadOnlyList<string> registrationTokens, string topic, string path)
101100
{
102101
this.ValidateRegistrationTokenList(registrationTokens);
103102

@@ -123,19 +122,20 @@ private async Task<TopicManagementResponse> SendInstanceIdRequest(
123122
return new TopicManagementResponse(response.Result);
124123
}
125124

126-
private void ValidateRegistrationTokenList(List<string> registrationTokens)
125+
private void ValidateRegistrationTokenList(IReadOnlyList<string> registrationTokens)
127126
{
128127
if (registrationTokens == null)
129128
{
130129
throw new ArgumentNullException("Registration token list must not be null");
131130
}
132131

133-
if (registrationTokens.Count() == 0)
132+
var count = registrationTokens.Count;
133+
if (count == 0)
134134
{
135135
throw new ArgumentException("Registration token list must not be empty");
136136
}
137137

138-
if (registrationTokens.Count() > 1000)
138+
if (count > 1000)
139139
{
140140
throw new ArgumentException("Registration token list must not contain more than 1000 tokens");
141141
}
@@ -167,7 +167,7 @@ private class InstanceIdServiceRequest
167167
public string Topic { get; set; }
168168

169169
[JsonProperty("registration_tokens")]
170-
public List<string> RegistrationTokens { get; set; }
170+
public IEnumerable<string> RegistrationTokens { get; set; }
171171
}
172172
}
173173
}

FirebaseAdmin/FirebaseAdmin/Messaging/InstanceIdServiceResponse.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ namespace FirebaseAdmin.Messaging
66
{
77
/// <summary>
88
/// Response from an operation that subscribes or unsubscribes registration tokens to a topic.
9-
/// See <see cref="FirebaseMessaging.SubscribeToTopicAsync(string, List{string})"/> and <see cref="FirebaseMessaging.UnsubscribeFromTopicAsync(string, List{string})"/>.
9+
/// See <see cref="FirebaseMessaging.SubscribeToTopicAsync(IReadOnlyList{string}, string)"/> and
10+
/// <see cref="FirebaseMessaging.UnsubscribeFromTopicAsync(IReadOnlyList{string}, string)"/>.
1011
/// </summary>
1112
internal class InstanceIdServiceResponse
1213
{

0 commit comments

Comments
 (0)