Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FirebaseAdmin.Messaging;
Expand Down Expand Up @@ -120,5 +121,21 @@ public async Task SendMulticast()
Assert.NotNull(response.Responses[0].Exception);
Assert.NotNull(response.Responses[1].Exception);
}

[Fact]
public async Task SubscribeToTopic()
{
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync("test-topic", new List<string> { "token1", "token2" });
Assert.NotNull(response);
Assert.Equal(2, response.FailureCount);
}

[Fact]
public async Task UnsubscribeFromTopic()
{
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync("test-topic", new List<string> { "token1", "token2" });
Assert.NotNull(response);
Assert.Equal(2, response.FailureCount);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using FirebaseAdmin.Tests;
Expand Down Expand Up @@ -118,6 +119,56 @@ await Assert.ThrowsAsync<OperationCanceledException>(
new Message() { Topic = "test-topic" }, canceller.Token));
}

[Fact]
public async Task SubscribeWithClientFactory()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var app = FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromAccessToken("test-token"),
HttpClientFactory = factory,
ProjectId = "test-project",
});
FirebaseMessaging messaging = FirebaseMessaging.GetMessaging(app);
Assert.NotNull(messaging);
Assert.Same(messaging, FirebaseMessaging.GetMessaging(app));

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

[Fact]
public async Task UnsubscribeWithClientFactory()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var app = FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromAccessToken("test-token"),
HttpClientFactory = factory,
ProjectId = "test-project",
});
FirebaseMessaging messaging = FirebaseMessaging.GetMessaging(app);
Assert.NotNull(messaging);
Assert.Same(messaging, FirebaseMessaging.GetMessaging(app));

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

public void Dispose()
{
FirebaseApp.DeleteAll();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Http;
using Xunit;

namespace FirebaseAdmin.Tests.Messaging
{
public class InstanceIdClientTest
{
private static readonly GoogleCredential MockCredential =
GoogleCredential.FromAccessToken("test-token");

[Fact]
public void InstanceIdClientThrowsOnNoProjectId()
{
var clientFactory = new HttpClientFactory();
Assert.Throws<ArgumentException>(
() => new InstanceIdClient(clientFactory, MockCredential, null));
Assert.Throws<ArgumentException>(
() => new InstanceIdClient(clientFactory, MockCredential, string.Empty));
}

[Fact]
public void InstanceIdClientThrowsOnNoCredential()
{
var clientFactory = new HttpClientFactory();
Assert.Throws<ArgumentNullException>(
() => new InstanceIdClient(clientFactory, null, "test-project"));
}

[Fact]
public void InstanceIdClientThrowsOnNoClientFactory()
{
var clientFactory = new HttpClientFactory();
Assert.Throws<ArgumentNullException>(
() => new InstanceIdClient(null, MockCredential, "test-project"));
}

[Fact]
public async Task InstanceIdClientSubscribesToTopic()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential, "test-project");

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

Assert.Equal(1, result.SuccessCount);
}

[Fact]
public async Task InstanceIdClientUnsubscribesFromTopic()
{
var handler = new MockMessageHandler()
{
Response = @"{""results"":[{}]}",
};
var factory = new MockHttpClientFactory(handler);

var client = new InstanceIdClient(factory, MockCredential, "test-project");

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

Assert.Equal(1, result.SuccessCount);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Xunit;

namespace FirebaseAdmin.Tests.Messaging
{
public class TopicManagementResponseTest
{
[Fact]
public void SuccessfulTopicManagementResponse()
{
var json = @"[{}, {}]";
var jObjects = JArray.Parse(json).ToObject<List<JObject>>();
var response = new TopicManagementResponse(jObjects);

Assert.Equal(0, response.FailureCount);
Assert.Equal(2, response.SuccessCount);
}

[Fact]
public void UnsuccessfulTopicManagementResponse()
{
var json = @"[{}, {""error"":""NOT_FOUND""}]";
var jObjects = JArray.Parse(json).ToObject<List<JObject>>();
var response = new TopicManagementResponse(jObjects);

Assert.Equal(1, response.FailureCount);
Assert.Equal(1, response.SuccessCount);
}

[Fact]
public void TopicManagementResponseCannotBeNull()
{
Assert.Throws<ArgumentException>(() =>
{
List<JObject> jObjects = null;
var response = new TopicManagementResponse(jObjects);
});
}

[Fact]
public void TopicManagementResponseCannotBeEmptyArray()
{
Assert.Throws<ArgumentException>(() =>
{
List<JObject> jObjects = new List<JObject>();
var response = new TopicManagementResponse(jObjects);
});
}

[Fact]
public void TopicManagementResponseHandlesUnknownErrors()
{
var json = @"[{""error"":""NOT_A_REAL_ERROR_CODE""}]";
var jObjects = JArray.Parse(json).ToObject<List<JObject>>();
var response = new TopicManagementResponse(jObjects);

Assert.Equal(1, response.FailureCount);
Assert.Equal(0, response.SuccessCount);
}

[Fact]
public void TopicManagementResponseHandlesUnexpectedResponse()
{
var json = @"[{""unexpected"":""NOT_A_REAL_CODE""}]";
var jObjects = JArray.Parse(json).ToObject<List<JObject>>();
var response = new TopicManagementResponse(jObjects);

Assert.Equal(0, response.FailureCount);
Assert.Equal(1, response.SuccessCount);
}

[Fact]
public void TopicManagementResponseCountsSuccessAndErrors()
{
var json = @"[{""error"": ""NOT_FOUND""}, {}, {""error"": ""NOT_FOUND""}, {}, {}]";
var jObjects = JArray.Parse(json).ToObject<List<JObject>>();
var response = new TopicManagementResponse(jObjects);

Assert.Equal(2, response.FailureCount);
Assert.Equal(3, response.SuccessCount);
}
}
}
27 changes: 27 additions & 0 deletions FirebaseAdmin/FirebaseAdmin/Messaging/FirebaseMessaging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ namespace FirebaseAdmin.Messaging
public sealed class FirebaseMessaging : IFirebaseService
{
private readonly FirebaseMessagingClient messagingClient;
private readonly InstanceIdClient instanceIdClient;

private FirebaseMessaging(FirebaseApp app)
{
this.messagingClient = new FirebaseMessagingClient(
app.Options.HttpClientFactory, app.Options.Credential, app.GetProjectId());

this.instanceIdClient = new InstanceIdClient(
app.Options.HttpClientFactory, app.Options.Credential, app.GetProjectId());
}

/// <summary>
Expand Down Expand Up @@ -310,12 +314,35 @@ public async Task<BatchResponse> SendMulticastAsync(
message.GetMessageList(), dryRun, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Subscribes a list of registration tokens to a topic.
/// </summary>
/// <param name="topic">The topic name to subscribe to. /topics/ will be prepended to the topic name provided if absent.</param>
/// <param name="registrationTokens">A list of registration tokens to subscribe.</param>
/// <returns>A task that completes with a <see cref="TopicManagementResponse"/>, giving details about the topic subscription operations.</returns>
public async Task<TopicManagementResponse> SubscribeToTopicAsync(string topic, List<string> registrationTokens)
{
return await this.instanceIdClient.SubscribeToTopicAsync(topic, registrationTokens);
}

/// <summary>
/// Unsubscribes a list of registration tokens from a topic.
/// </summary>
/// <param name="topic">The topic name to unsubscribe from. /topics/ will be prepended to the topic name provided if absent.</param>
/// <param name="registrationTokens">A list of registration tokens to unsubscribe.</param>
/// <returns>A task that completes with a <see cref="TopicManagementResponse"/>, giving details about the topic unsubscription operations.</returns>
public async Task<TopicManagementResponse> UnsubscribeFromTopicAsync(string topic, List<string> registrationTokens)
{
return await this.instanceIdClient.UnsubscribeFromTopicAsync(topic, registrationTokens);
}

/// <summary>
/// Deletes this <see cref="FirebaseMessaging"/> service instance.
/// </summary>
void IFirebaseService.Delete()
{
this.messagingClient.Dispose();
this.instanceIdClient.Dispose();
}
}
}
Loading