Skip to content

Commit 9f46c5e

Browse files
committed
Added base class for all HTTP error handling
1 parent 0dc82a3 commit 9f46c5e

File tree

3 files changed

+183
-46
lines changed

3 files changed

+183
-46
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2019, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Collections.Generic;
16+
using System.Net;
17+
using System.Net.Http;
18+
using System.Text;
19+
using Xunit;
20+
21+
namespace FirebaseAdmin.Tests
22+
{
23+
public class HttpErrorHandlerTest
24+
{
25+
public static readonly IEnumerable<object[]> HttpErrorCodes =
26+
new List<object[]>()
27+
{
28+
new object[] { HttpStatusCode.BadRequest, ErrorCode.InvalidArgument },
29+
new object[] { HttpStatusCode.Unauthorized, ErrorCode.Unauthenticated },
30+
new object[] { HttpStatusCode.Forbidden, ErrorCode.PermissionDenied },
31+
new object[] { HttpStatusCode.NotFound, ErrorCode.NotFound },
32+
new object[] { HttpStatusCode.Conflict, ErrorCode.Conflict },
33+
new object[] { (HttpStatusCode)429, ErrorCode.ResourceExhausted },
34+
new object[] { HttpStatusCode.InternalServerError, ErrorCode.Internal },
35+
new object[] { HttpStatusCode.ServiceUnavailable, ErrorCode.Unavailable },
36+
};
37+
38+
[Theory]
39+
[MemberData(nameof(HttpErrorCodes))]
40+
public void KnownHttpStatusCode(HttpStatusCode statusCode, ErrorCode expected)
41+
{
42+
var json = "{}";
43+
var resp = new HttpResponseMessage()
44+
{
45+
StatusCode = statusCode,
46+
Content = new StringContent(json, Encoding.UTF8, "application/json"),
47+
};
48+
49+
var handler = new HttpErrorHandler();
50+
var error = Assert.Throws<FirebaseException>(() => handler.ThrowIfError(resp, json));
51+
52+
Assert.Equal(expected, error.ErrorCode);
53+
Assert.Equal(
54+
$"Unexpected HTTP response with status: {(int)statusCode} ({statusCode})\n{json}",
55+
error.Message);
56+
Assert.Same(resp, error.HttpResponse);
57+
Assert.Null(error.InnerException);
58+
}
59+
60+
[Fact]
61+
public void UnknownHttpStatusCode()
62+
{
63+
var json = "{}";
64+
var resp = new HttpResponseMessage()
65+
{
66+
StatusCode = HttpStatusCode.MethodNotAllowed,
67+
Content = new StringContent(json, Encoding.UTF8, "application/json"),
68+
};
69+
70+
var handler = new HttpErrorHandler();
71+
var error = Assert.Throws<FirebaseException>(() => handler.ThrowIfError(resp, json));
72+
73+
Assert.Equal(ErrorCode.Unknown, error.ErrorCode);
74+
Assert.Equal(
75+
$"Unexpected HTTP response with status: 405 (MethodNotAllowed)\n{json}",
76+
error.Message);
77+
Assert.Same(resp, error.HttpResponse);
78+
Assert.Null(error.InnerException);
79+
}
80+
}
81+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2019, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using System.Collections.Generic;
17+
using System.Net;
18+
using System.Net.Http;
19+
20+
namespace FirebaseAdmin
21+
{
22+
internal class HttpErrorHandler
23+
{
24+
private static readonly IReadOnlyDictionary<HttpStatusCode, ErrorCode> HttpErrorCodes =
25+
new Dictionary<HttpStatusCode, ErrorCode>()
26+
{
27+
{ HttpStatusCode.BadRequest, ErrorCode.InvalidArgument },
28+
{ HttpStatusCode.Unauthorized, ErrorCode.Unauthenticated },
29+
{ HttpStatusCode.Forbidden, ErrorCode.PermissionDenied },
30+
{ HttpStatusCode.NotFound, ErrorCode.NotFound },
31+
{ HttpStatusCode.Conflict, ErrorCode.Conflict },
32+
{ (HttpStatusCode)429, ErrorCode.ResourceExhausted },
33+
{ HttpStatusCode.InternalServerError, ErrorCode.Internal },
34+
{ HttpStatusCode.ServiceUnavailable, ErrorCode.Unavailable },
35+
};
36+
37+
internal void ThrowIfError(HttpResponseMessage response, string json)
38+
{
39+
if (response.IsSuccessStatusCode)
40+
{
41+
return;
42+
}
43+
44+
var info = this.ExtractErrorInfo(response, json);
45+
var args = new FirebaseExceptionArgs()
46+
{
47+
Code = info.Code,
48+
Message = info.Message,
49+
HttpResponse = response,
50+
ResponseBody = json,
51+
};
52+
throw this.CreateException(args);
53+
}
54+
55+
protected virtual ErrorInfo ExtractErrorInfo(HttpResponseMessage response, string json)
56+
{
57+
ErrorCode code;
58+
if (!HttpErrorCodes.TryGetValue(response.StatusCode, out code))
59+
{
60+
code = ErrorCode.Unknown;
61+
}
62+
63+
var message = "Unexpected HTTP response with status: "
64+
+ $"{(int)response.StatusCode} ({response.StatusCode})"
65+
+ $"{Environment.NewLine}{json}";
66+
return new ErrorInfo()
67+
{
68+
Code = code,
69+
Message = message,
70+
};
71+
}
72+
73+
protected virtual FirebaseException CreateException(FirebaseExceptionArgs args)
74+
{
75+
return new FirebaseException(args.Code, args.Message, response: args.HttpResponse);
76+
}
77+
78+
internal sealed class ErrorInfo
79+
{
80+
internal ErrorCode Code { get; set; }
81+
82+
internal string Message { get; set; }
83+
}
84+
85+
internal sealed class FirebaseExceptionArgs
86+
{
87+
internal ErrorCode Code { get; set; }
88+
89+
internal string Message { get; set; }
90+
91+
internal HttpResponseMessage HttpResponse { get; set; }
92+
93+
internal string ResponseBody { get; set; }
94+
}
95+
}
96+
}

FirebaseAdmin/FirebaseAdmin/PlatformErrorHandler.cs

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
using System;
1615
using System.Collections.Generic;
17-
using System.Net;
1816
using System.Net.Http;
1917
using Google.Apis.Json;
2018
using Newtonsoft.Json;
2119

2220
namespace FirebaseAdmin
2321
{
24-
internal class PlatformErrorHandler
22+
internal class PlatformErrorHandler : HttpErrorHandler
2523
{
2624
private static readonly IReadOnlyDictionary<string, ErrorCode> PlatformErrorCodes =
2725
new Dictionary<string, ErrorCode>()
@@ -33,56 +31,29 @@ internal class PlatformErrorHandler
3331
{ "UNAVAILABLE", ErrorCode.Unavailable },
3432
};
3533

36-
private static readonly IReadOnlyDictionary<HttpStatusCode, ErrorCode> HttpErrorCodes =
37-
new Dictionary<HttpStatusCode, ErrorCode>()
38-
{
39-
{ HttpStatusCode.BadRequest, ErrorCode.InvalidArgument },
40-
{ HttpStatusCode.InternalServerError, ErrorCode.Internal },
41-
{ HttpStatusCode.Forbidden, ErrorCode.PermissionDenied },
42-
{ HttpStatusCode.Unauthorized, ErrorCode.Unauthenticated },
43-
{ HttpStatusCode.ServiceUnavailable, ErrorCode.Unavailable },
44-
};
45-
46-
internal void ThrowIfError(HttpResponseMessage response, string json)
34+
protected override ErrorInfo ExtractErrorInfo(HttpResponseMessage response, string json)
4735
{
48-
if (response.IsSuccessStatusCode)
49-
{
50-
return;
51-
}
52-
5336
var parsedResponse = NewtonsoftJsonSerializer.Instance.Deserialize<PlatformErrorResponse>(json);
5437
var status = parsedResponse.Error?.Status ?? string.Empty;
38+
var defaults = base.ExtractErrorInfo(response, json);
5539

5640
ErrorCode code;
5741
if (!PlatformErrorCodes.TryGetValue(status, out code))
5842
{
59-
if (!HttpErrorCodes.TryGetValue(response.StatusCode, out code))
60-
{
61-
code = ErrorCode.Unknown;
62-
}
43+
code = defaults.Code;
6344
}
6445

6546
var message = parsedResponse.Error?.Message;
6647
if (string.IsNullOrEmpty(message))
6748
{
68-
message = "Unexpected HTTP response with status: "
69-
+ $"{(int)response.StatusCode} ({response.StatusCode})"
70-
+ $"{Environment.NewLine}{json}";
49+
message = defaults.Message;
7150
}
7251

73-
var args = new FirebaseExceptionArgs()
52+
return new ErrorInfo()
7453
{
7554
Code = code,
7655
Message = message,
77-
HttpResponse = response,
78-
ResponseBody = json,
7956
};
80-
throw this.CreateException(args);
81-
}
82-
83-
protected virtual FirebaseException CreateException(FirebaseExceptionArgs args)
84-
{
85-
return new FirebaseException(args.Code, args.Message, response: args.HttpResponse);
8657
}
8758

8859
internal class PlatformErrorResponse
@@ -99,16 +70,5 @@ internal class PlatformError
9970
[JsonProperty("message")]
10071
public string Message { get; set; }
10172
}
102-
103-
internal class FirebaseExceptionArgs
104-
{
105-
internal ErrorCode Code { get; set; }
106-
107-
internal string Message { get; set; }
108-
109-
internal HttpResponseMessage HttpResponse { get; set; }
110-
111-
internal string ResponseBody { get; set; }
112-
}
11373
}
11474
}

0 commit comments

Comments
 (0)