Skip to content

Commit 7c972d8

Browse files
authored
fix(fcm): Fixed error handling in the topic management APIs (#155)
1 parent 9e5a8e0 commit 7c972d8

File tree

2 files changed

+70
-87
lines changed

2 files changed

+70
-87
lines changed

FirebaseAdmin/FirebaseAdmin.Tests/Messaging/InstanceIdClientTest.cs

Lines changed: 25 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ namespace FirebaseAdmin.Tests.Messaging
1313
{
1414
public class InstanceIdClientTest
1515
{
16+
public static readonly IEnumerable<object[]> ErrorCodes =
17+
new List<object[]>()
18+
{
19+
new object[] { HttpStatusCode.BadRequest, ErrorCode.InvalidArgument },
20+
new object[] { HttpStatusCode.Unauthorized, ErrorCode.Unauthenticated },
21+
new object[] { HttpStatusCode.Forbidden, ErrorCode.PermissionDenied },
22+
new object[] { HttpStatusCode.NotFound, ErrorCode.NotFound },
23+
new object[] { HttpStatusCode.ServiceUnavailable, ErrorCode.Unavailable },
24+
};
25+
1626
private static readonly GoogleCredential MockCredential =
1727
GoogleCredential.FromAccessToken("test-token");
1828

@@ -64,37 +74,14 @@ public async Task UnsubscribeFromTopicAsync()
6474
Assert.Equal(1, result.SuccessCount);
6575
}
6676

67-
[Fact]
68-
public async Task BadRequest()
69-
{
70-
var handler = new MockMessageHandler()
71-
{
72-
StatusCode = HttpStatusCode.BadRequest,
73-
Response = "BadRequest",
74-
};
75-
var factory = new MockHttpClientFactory(handler);
76-
77-
var client = new InstanceIdClient(factory, MockCredential);
78-
79-
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
80-
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
81-
82-
Assert.Equal(ErrorCode.InvalidArgument, exception.ErrorCode);
83-
Assert.Equal(
84-
$"Unexpected HTTP response with status: 400 (BadRequest){Environment.NewLine}BadRequest",
85-
exception.Message);
86-
Assert.Null(exception.MessagingErrorCode);
87-
Assert.NotNull(exception.HttpResponse);
88-
Assert.Null(exception.InnerException);
89-
}
90-
91-
[Fact]
92-
public async Task Unauthorized()
77+
[Theory]
78+
[MemberData(nameof(ErrorCodes))]
79+
public async Task ErrorResponse(HttpStatusCode statusCode, ErrorCode expected)
9380
{
9481
var handler = new MockMessageHandler()
9582
{
96-
StatusCode = HttpStatusCode.Unauthorized,
97-
Response = "Unauthorized",
83+
StatusCode = statusCode,
84+
Response = @"{""error"":""ErrorCode""}",
9885
};
9986
var factory = new MockHttpClientFactory(handler);
10087

@@ -103,22 +90,23 @@ public async Task Unauthorized()
10390
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
10491
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
10592

106-
Assert.Equal(ErrorCode.Unauthenticated, exception.ErrorCode);
93+
Assert.Equal(expected, exception.ErrorCode);
10794
Assert.Equal(
108-
$"Unexpected HTTP response with status: 401 (Unauthorized){Environment.NewLine}Unauthorized",
95+
"Error while calling the IID service: ErrorCode",
10996
exception.Message);
11097
Assert.Null(exception.MessagingErrorCode);
11198
Assert.NotNull(exception.HttpResponse);
11299
Assert.Null(exception.InnerException);
113100
}
114101

115-
[Fact]
116-
public async Task Forbidden()
102+
[Theory]
103+
[MemberData(nameof(ErrorCodes))]
104+
public async Task UnexpectedErrorResponse(HttpStatusCode statusCode, ErrorCode expected)
117105
{
118106
var handler = new MockMessageHandler()
119107
{
120-
StatusCode = HttpStatusCode.Forbidden,
121-
Response = "Forbidden",
108+
StatusCode = statusCode,
109+
Response = "NonJsonErrorResponse",
122110
};
123111
var factory = new MockHttpClientFactory(handler);
124112

@@ -127,64 +115,16 @@ public async Task Forbidden()
127115
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
128116
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
129117

130-
Assert.Equal(ErrorCode.PermissionDenied, exception.ErrorCode);
118+
Assert.Equal(expected, exception.ErrorCode);
131119
Assert.Equal(
132-
$"Unexpected HTTP response with status: 403 (Forbidden){Environment.NewLine}Forbidden",
120+
"Unexpected HTTP response with status: "
121+
+ $"{(int)statusCode} ({statusCode}){Environment.NewLine}NonJsonErrorResponse",
133122
exception.Message);
134123
Assert.Null(exception.MessagingErrorCode);
135124
Assert.NotNull(exception.HttpResponse);
136125
Assert.Null(exception.InnerException);
137126
}
138127

139-
[Fact]
140-
public async Task NotFound()
141-
{
142-
var handler = new MockMessageHandler()
143-
{
144-
StatusCode = HttpStatusCode.NotFound,
145-
Response = "NotFound",
146-
};
147-
var factory = new MockHttpClientFactory(handler);
148-
149-
var client = new InstanceIdClient(factory, MockCredential);
150-
151-
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
152-
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
153-
154-
Assert.Equal(ErrorCode.NotFound, exception.ErrorCode);
155-
Assert.Equal(
156-
$"Unexpected HTTP response with status: 404 (NotFound){Environment.NewLine}NotFound",
157-
exception.Message);
158-
Assert.Null(exception.MessagingErrorCode);
159-
Assert.NotNull(exception.HttpResponse);
160-
Assert.Null(exception.InnerException);
161-
}
162-
163-
[Fact]
164-
public async Task ServiceUnavailable()
165-
{
166-
var handler = new MockMessageHandler()
167-
{
168-
StatusCode = HttpStatusCode.ServiceUnavailable,
169-
Response = "ServiceUnavailable",
170-
};
171-
var factory = new MockHttpClientFactory(handler);
172-
173-
var client = new InstanceIdClient(factory, MockCredential, RetryOptions.NoBackOff);
174-
175-
var exception = await Assert.ThrowsAsync<FirebaseMessagingException>(
176-
() => client.SubscribeToTopicAsync(new List<string> { "abc123" }, "test-topic"));
177-
178-
Assert.Equal(ErrorCode.Unavailable, exception.ErrorCode);
179-
Assert.Equal(
180-
$"Unexpected HTTP response with status: 503 (ServiceUnavailable){Environment.NewLine}ServiceUnavailable",
181-
exception.Message);
182-
Assert.Null(exception.MessagingErrorCode);
183-
Assert.NotNull(exception.HttpResponse);
184-
Assert.Null(exception.InnerException);
185-
Assert.Equal(5, handler.Calls);
186-
}
187-
188128
[Fact]
189129
public async Task TransportError()
190130
{

FirebaseAdmin/FirebaseAdmin/Messaging/InstanceIdClient.cs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ internal InstanceIdClient(
5454
HttpClientFactory = clientFactory.ThrowIfNull(nameof(clientFactory)),
5555
Credential = credential.ThrowIfNull(nameof(credential)),
5656
RequestExceptionHandler = MessagingErrorHandler.Instance,
57-
ErrorResponseHandler = MessagingErrorHandler.Instance,
57+
ErrorResponseHandler = InstanceIdErrorHandler.Instance,
5858
DeserializeExceptionHandler = MessagingErrorHandler.Instance,
5959
RetryOptions = retryOptions,
6060
});
@@ -180,5 +180,48 @@ private class InstanceIdServiceRequest
180180
[JsonProperty("registration_tokens")]
181181
public IEnumerable<string> RegistrationTokens { get; set; }
182182
}
183-
}
183+
184+
private class InstanceIdServiceError
185+
{
186+
[JsonProperty("error")]
187+
public string ErrorCode { get; set; }
188+
}
189+
190+
private class InstanceIdErrorHandler : HttpErrorHandler<FirebaseMessagingException>
191+
{
192+
internal static readonly InstanceIdErrorHandler Instance = new InstanceIdErrorHandler();
193+
194+
private InstanceIdErrorHandler() { }
195+
196+
protected override FirebaseMessagingException CreateException(FirebaseExceptionArgs args)
197+
{
198+
var errorCode = this.GetErrorCode(args.ResponseBody);
199+
var message = args.Message;
200+
if (!string.IsNullOrEmpty(errorCode))
201+
{
202+
message = $"Error while calling the IID service: {errorCode}";
203+
}
204+
205+
return new FirebaseMessagingException(
206+
args.Code,
207+
message,
208+
null,
209+
response: args.HttpResponse);
210+
}
211+
212+
private string GetErrorCode(string response)
213+
{
214+
try
215+
{
216+
var iidError = NewtonsoftJsonSerializer.Instance.Deserialize<InstanceIdServiceError>(
217+
response);
218+
return iidError.ErrorCode;
219+
}
220+
catch
221+
{
222+
return null;
223+
}
224+
}
225+
}
226+
}
184227
}

0 commit comments

Comments
 (0)