Skip to content

Commit 63ef92a

Browse files
Merge pull request #29 from notion-dotnet/feature/parse-error-response-and-make-it-available-in-exception
Parse error response and make it available in exception
2 parents b476e81 + 84c61cf commit 63ef92a

File tree

4 files changed

+109
-46
lines changed

4 files changed

+109
-46
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace Notion.Client
4+
{
5+
public enum NotionAPIErrorCode
6+
{
7+
[EnumMember(Value = "invalid_json")]
8+
InvalidJSON,
9+
10+
[EnumMember(Value = "invalid_request_url")]
11+
InvalidRequestURL,
12+
13+
[EnumMember(Value = "invalid_request")]
14+
InvalidRequest,
15+
16+
[EnumMember(Value = "validation_error")]
17+
ValidationError,
18+
19+
[EnumMember(Value = "missing_version")]
20+
MissingVersion,
21+
22+
[EnumMember(Value = "unauthorized")]
23+
Unauthorized,
24+
25+
[EnumMember(Value = "restricted_resource")]
26+
RestrictedResource,
27+
28+
[EnumMember(Value = "object_not_found")]
29+
ObjectNotFound,
30+
31+
[EnumMember(Value = "conflict_error")]
32+
ConflictError,
33+
34+
[EnumMember(Value = "rate_limited")]
35+
RateLimited,
36+
37+
[EnumMember(Value = "internal_server_error")]
38+
InternalServerError,
39+
40+
[EnumMember(Value = "service_unavailable")]
41+
ServiceUnavailable,
42+
}
43+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Notion.Client
2+
{
3+
class NotionApiErrorResponse
4+
{
5+
public NotionAPIErrorCode ErrorCode { get; set; }
6+
public string Message { get; set; }
7+
}
8+
}

Src/Notion.Client/NotionApiException.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,32 @@
33

44
namespace Notion.Client
55
{
6-
class NotionApiException : Exception
6+
public class NotionApiException : Exception
77
{
8-
public NotionApiException(HttpStatusCode statusCode, string message) : this(statusCode, message, null)
8+
public NotionApiException(HttpStatusCode statusCode, NotionAPIErrorCode? notionAPIErrorCode, string message)
9+
: this(statusCode, notionAPIErrorCode, message, null)
910
{
1011
}
1112

12-
public NotionApiException(HttpStatusCode statusCode, string message, Exception innerException) : base(message, innerException)
13+
public NotionApiException(HttpStatusCode statusCode, string message)
14+
: this(statusCode, null, message, null)
1315
{
16+
}
17+
18+
public NotionApiException(
19+
HttpStatusCode statusCode,
20+
NotionAPIErrorCode? notionAPIErrorCode,
21+
string message,
22+
Exception innerException) : base(message, innerException)
23+
{
24+
NotionAPIErrorCode = notionAPIErrorCode;
25+
StatusCode = statusCode;
26+
1427
Data.Add("StatusCode", statusCode);
28+
Data.Add("NotionApiErrorCode", NotionAPIErrorCode);
1529
}
30+
31+
public NotionAPIErrorCode? NotionAPIErrorCode { get; }
32+
public HttpStatusCode StatusCode { get; }
1633
}
1734
}

Src/Notion.Client/RestClient/RestClient.cs

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,42 @@ public async Task<T> GetAsync<T>(
4848
JsonSerializerSettings serializerSettings = null,
4949
CancellationToken cancellationToken = default)
5050
{
51-
EnsureHttpClient();
51+
var response = await SendAsync(uri, HttpMethod.Get, queryParams, headers, cancellationToken: cancellationToken);
5252

53-
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
53+
return await response.ParseStreamAsync<T>(serializerSettings);
54+
}
5455

55-
var response = await SendAsync(requestUri, HttpMethod.Get, headers, cancellationToken: cancellationToken);
56+
private static async Task<Exception> BuildException(HttpResponseMessage response)
57+
{
58+
var errorBody = await response.Content.ReadAsStringAsync();
5659

57-
if (response.IsSuccessStatusCode)
60+
NotionApiErrorResponse errorResponse = null;
61+
if (!string.IsNullOrWhiteSpace(errorBody))
5862
{
59-
return await response.ParseStreamAsync<T>(serializerSettings);
63+
try
64+
{
65+
errorResponse = JsonConvert.DeserializeObject<NotionApiErrorResponse>(errorBody);
66+
}
67+
catch
68+
{
69+
}
6070
}
6171

62-
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
63-
? response.ReasonPhrase
64-
: await response.Content.ReadAsStringAsync();
65-
66-
throw new NotionApiException(response.StatusCode, message);
72+
return new NotionApiException(response.StatusCode, errorResponse?.ErrorCode, errorResponse.Message);
6773
}
6874

6975
private async Task<HttpResponseMessage> SendAsync(
7076
string requestUri,
7177
HttpMethod httpMethod,
78+
IDictionary<string, string> queryParams = null,
7279
IDictionary<string, string> headers = null,
7380
Action<HttpRequestMessage> attachContent = null,
7481
CancellationToken cancellationToken = default)
7582
{
83+
EnsureHttpClient();
84+
85+
requestUri = AddQueryString(requestUri, queryParams);
86+
7687
HttpRequestMessage httpRequest = new HttpRequestMessage(httpMethod, requestUri);
7788
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
7889
httpRequest.Headers.Add("Notion-Version", _options.NotionVersion);
@@ -84,7 +95,14 @@ private async Task<HttpResponseMessage> SendAsync(
8495

8596
attachContent?.Invoke(httpRequest);
8697

87-
return await _httpClient.SendAsync(httpRequest, cancellationToken);
98+
var response = await _httpClient.SendAsync(httpRequest, cancellationToken);
99+
100+
if (!response.IsSuccessStatusCode)
101+
{
102+
throw await BuildException(response);
103+
}
104+
105+
return response;
88106
}
89107

90108
private static void AddHeaders(HttpRequestMessage request, IDictionary<string, string> headers)
@@ -103,55 +121,27 @@ public async Task<T> PostAsync<T>(
103121
JsonSerializerSettings serializerSettings = null,
104122
CancellationToken cancellationToken = default)
105123
{
106-
EnsureHttpClient();
107-
108124
void AttachContent(HttpRequestMessage httpRequest)
109125
{
110126
httpRequest.Content = new StringContent(JsonConvert.SerializeObject(body, defaultSerializerSettings), Encoding.UTF8, "application/json");
111127
}
112128

113-
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
114-
115-
var response = await SendAsync(requestUri, HttpMethod.Post, headers, AttachContent, cancellationToken: cancellationToken);
116-
117-
if (response.IsSuccessStatusCode)
118-
{
119-
return await response.ParseStreamAsync<T>(serializerSettings);
120-
}
121-
122-
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
123-
? response.ReasonPhrase
124-
: await response.Content.ReadAsStringAsync();
129+
var response = await SendAsync(uri, HttpMethod.Post, queryParams, headers, AttachContent, cancellationToken: cancellationToken);
125130

126-
throw new NotionApiException(response.StatusCode, message);
131+
return await response.ParseStreamAsync<T>(serializerSettings);
127132
}
128133

129134
public async Task<T> PatchAsync<T>(string uri, object body, IDictionary<string, string> queryParams = null, IDictionary<string, string> headers = null, JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default)
130135
{
131-
EnsureHttpClient();
132-
133136
void AttachContent(HttpRequestMessage httpRequest)
134137
{
135138
var serializedBody = JsonConvert.SerializeObject(body, defaultSerializerSettings);
136139
httpRequest.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json");
137140
}
138141

139-
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
142+
var response = await SendAsync(uri, new HttpMethod("PATCH"), queryParams, headers, AttachContent, cancellationToken: cancellationToken);
140143

141-
var response = await SendAsync(requestUri, new HttpMethod("PATCH"), headers, AttachContent, cancellationToken: cancellationToken);
142-
143-
if (response.IsSuccessStatusCode)
144-
{
145-
return await response.ParseStreamAsync<T>(serializerSettings);
146-
}
147-
148-
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
149-
? response.ReasonPhrase
150-
: await response.Content.ReadAsStringAsync();
151-
152-
var errorMessage = await response.Content.ReadAsStringAsync();
153-
154-
throw new NotionApiException(response.StatusCode, message);
144+
return await response.ParseStreamAsync<T>(serializerSettings);
155145
}
156146

157147
private HttpClient EnsureHttpClient()
@@ -164,5 +154,10 @@ private HttpClient EnsureHttpClient()
164154

165155
return _httpClient;
166156
}
157+
158+
private static string AddQueryString(string uri, IDictionary<string, string> queryParams)
159+
{
160+
return queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
161+
}
167162
}
168163
}

0 commit comments

Comments
 (0)