Skip to content

Commit 9d7967a

Browse files
Merge pull request #16 from notion-dotnet/code-refactor
Code refactor ♻️
2 parents 91a5253 + 04d5fe9 commit 9d7967a

File tree

9 files changed

+208
-134
lines changed

9 files changed

+208
-134
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace Notion.Client
2+
{
3+
public static class ApiEndpoints
4+
{
5+
public static class DatabasesApiUrls
6+
{
7+
public static string Retrieve(string databaseId) => $"/v1/databases/{databaseId}";
8+
public static string List() => "/v1/databases";
9+
public static string Query(string databaseId) => $"/v1/databases/{databaseId}/query";
10+
}
11+
12+
public static class UsersApiUrls
13+
{
14+
public static string Retrieve(string userId) => $"/v1/users/{userId}";
15+
public static string List() => "/v1/users";
16+
}
17+
}
18+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using static Notion.Client.ApiEndpoints;
5+
6+
namespace Notion.Client
7+
{
8+
public interface IDatabasesClient
9+
{
10+
Task<Database> RetrieveAsync(string databaseId);
11+
Task<PaginatedList<Page>> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters);
12+
Task<PaginatedList<Database>> ListAsync(DatabasesListParameters databasesListParameters = null);
13+
}
14+
15+
public class DatabasesClient : IDatabasesClient
16+
{
17+
private readonly IRestClient _client;
18+
19+
public DatabasesClient(IRestClient client)
20+
{
21+
_client = client;
22+
}
23+
24+
public async Task<Database> RetrieveAsync(string databaseId)
25+
{
26+
return await _client.GetAsync<Database>(DatabasesApiUrls.Retrieve(databaseId));
27+
}
28+
29+
public async Task<PaginatedList<Database>> ListAsync(DatabasesListParameters databasesListParameters = null)
30+
{
31+
var databasesListQueryParmaters = (IDatabasesListQueryParmaters)databasesListParameters;
32+
33+
var queryParams = new Dictionary<string, string>()
34+
{
35+
{ "start_cursor", databasesListQueryParmaters?.StartCursor },
36+
{ "page_size", databasesListQueryParmaters?.PageSize }
37+
};
38+
39+
return await _client.GetAsync<PaginatedList<Database>>(DatabasesApiUrls.List(), queryParams);
40+
}
41+
42+
public async Task<PaginatedList<Page>> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters)
43+
{
44+
var body = (IDatabaseQueryBodyParameters)databasesQueryParameters;
45+
46+
return await _client.PostAsync<PaginatedList<Page>>(DatabasesApiUrls.Query(databaseId), body);
47+
}
48+
}
49+
}
Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Threading.Tasks;
3+
using static Notion.Client.ApiEndpoints;
34

45
namespace Notion.Client
56
{
@@ -20,26 +21,12 @@ public UsersClient(IRestClient client)
2021

2122
public async Task<User> RetrieveAsync(string userId)
2223
{
23-
try
24-
{
25-
return await _client.GetAsync<User>($"users/{userId}");
26-
}
27-
catch (Exception e)
28-
{
29-
return null;
30-
}
24+
return await _client.GetAsync<User>(UsersApiUrls.Retrieve(userId));
3125
}
3226

3327
public async Task<PaginatedList<User>> ListAsync()
3428
{
35-
try
36-
{
37-
return await _client.GetAsync<PaginatedList<User>>("users");
38-
}
39-
catch (Exception e)
40-
{
41-
return null;
42-
}
29+
return await _client.GetAsync<PaginatedList<User>>(UsersApiUrls.List());
4330
}
4431
}
4532
}

Src/Notion.Client/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
internal class Constants
44
{
5-
internal static string BASE_URL = "https://api.notion.com/v1/";
5+
internal static string BASE_URL = "https://api.notion.com/";
66
internal static string DEFAULT_NOTION_VERSION = "2021-05-13";
77
}
88
}

Src/Notion.Client/DatabasesClient.cs

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.IO;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using Newtonsoft.Json;
5+
6+
namespace Notion.Client.Extensions
7+
{
8+
internal static class HttpResponseMessageExtensions
9+
{
10+
internal static async Task<T> ParseStreamAsync<T>(this HttpResponseMessage response, JsonSerializerSettings serializerSettings = null)
11+
{
12+
using (Stream stream = await response.Content.ReadAsStreamAsync())
13+
{
14+
using (StreamReader streamReader = new StreamReader(stream))
15+
{
16+
using (JsonReader jsonReader = new JsonTextReader(streamReader))
17+
{
18+
JsonSerializer serializer = null;
19+
20+
if (serializerSettings == null)
21+
{
22+
serializer = JsonSerializer.CreateDefault();
23+
}
24+
else
25+
{
26+
serializer = JsonSerializer.Create(serializerSettings);
27+
}
28+
29+
return serializer.Deserialize<T>(jsonReader);
30+
}
31+
}
32+
}
33+
}
34+
}
35+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.Net;
3+
4+
namespace Notion.Client
5+
{
6+
class NotionApiException : Exception
7+
{
8+
public NotionApiException(HttpStatusCode statusCode, string message) : this(statusCode, message, null)
9+
{
10+
}
11+
12+
public NotionApiException(HttpStatusCode statusCode, string message, Exception innerException) : base(message, innerException)
13+
{
14+
Data.Add("StatusCode", statusCode);
15+
}
16+
}
17+
}

Src/Notion.Client/RestClient.cs

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,38 @@
33
using System.Net.Http;
44
using System.Net.Http.Headers;
55
using System.Text;
6+
using System.Threading;
67
using System.Threading.Tasks;
78
using Newtonsoft.Json;
9+
using Notion.Client.Extensions;
810
using Notion.Client.http;
911

1012
namespace Notion.Client
1113
{
1214
public interface IRestClient
1315
{
14-
Task<T> GetAsync<T>(string uri, Dictionary<string, string> queryParams = null);
15-
Task<T> PostAsync<T>(string uri, object body);
16+
Task<T> GetAsync<T>(
17+
string uri,
18+
IDictionary<string, string> queryParams = null,
19+
IDictionary<string, string> headers = null,
20+
JsonSerializerSettings serializerSettings = null,
21+
CancellationToken cancellationToken = default);
22+
23+
Task<T> PostAsync<T>(
24+
string uri,
25+
object body,
26+
IDictionary<string, string> queryParams = null,
27+
IDictionary<string, string> headers = null,
28+
JsonSerializerSettings serializerSettings = null,
29+
CancellationToken cancellationToken = default);
1630
}
1731

1832
public class RestClient : IRestClient
1933
{
2034
private HttpClient _httpClient;
2135
private readonly ClientOptions _options;
22-
private readonly List<JsonConverter> jsonConverters = new List<JsonConverter>();
2336

24-
private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings
37+
private readonly JsonSerializerSettings defaultSerializerSettings = new JsonSerializerSettings
2538
{
2639
NullValueHandling = NullValueHandling.Ignore
2740
};
@@ -41,32 +54,89 @@ private static ClientOptions MergeOptions(ClientOptions options)
4154
};
4255
}
4356

44-
public async Task<T> GetAsync<T>(string uri, Dictionary<string, string> queryParams = null)
57+
public async Task<T> GetAsync<T>(
58+
string uri,
59+
IDictionary<string, string> queryParams = null,
60+
IDictionary<string, string> headers = null,
61+
JsonSerializerSettings serializerSettings = null,
62+
CancellationToken cancellationToken = default)
4563
{
4664
EnsureHttpClient();
4765

48-
uri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
66+
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
4967

50-
using (var stream = await _httpClient.GetStreamAsync(uri))
68+
var response = await SendAsync(requestUri, HttpMethod.Get, headers, cancellationToken: cancellationToken);
69+
70+
if (response.IsSuccessStatusCode)
5171
{
52-
return SerializerHelper.Deserialize<T>(stream, jsonConverters);
72+
return await response.ParseStreamAsync<T>(serializerSettings);
5373
}
74+
75+
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
76+
? response.ReasonPhrase
77+
: await response.Content.ReadAsStringAsync();
78+
79+
throw new NotionApiException(response.StatusCode, message);
5480
}
5581

56-
public async Task<T> PostAsync<T>(string uri, object body)
82+
private async Task<HttpResponseMessage> SendAsync(
83+
string requestUri,
84+
HttpMethod httpMethod,
85+
IDictionary<string, string> headers = null,
86+
Action<HttpRequestMessage> attachContent = null,
87+
CancellationToken cancellationToken = default)
5788
{
58-
EnsureHttpClient();
89+
HttpRequestMessage httpRequest = new HttpRequestMessage(httpMethod, requestUri);
90+
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
91+
httpRequest.Headers.Add("Notion-Version", _options.NotionVersion);
92+
93+
if (headers != null)
94+
{
95+
AddHeaders(httpRequest, headers);
96+
}
97+
98+
attachContent?.Invoke(httpRequest);
99+
100+
return await _httpClient.SendAsync(httpRequest, cancellationToken);
101+
}
102+
103+
private static void AddHeaders(HttpRequestMessage request, IDictionary<string, string> headers)
104+
{
105+
foreach (var header in headers)
106+
{
107+
request.Headers.Add(header.Key, header.Value);
108+
}
109+
}
59110

60-
var content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, "application/json");
111+
public async Task<T> PostAsync<T>(
112+
string uri,
113+
object body,
114+
IDictionary<string, string> queryParams = null,
115+
IDictionary<string, string> headers = null,
116+
JsonSerializerSettings serializerSettings = null,
117+
CancellationToken cancellationToken = default)
118+
{
119+
EnsureHttpClient();
61120

62-
using (var response = await _httpClient.PostAsync(uri, content))
121+
void AttachContent(HttpRequestMessage httpRequest)
63122
{
64-
response.EnsureSuccessStatusCode();
123+
httpRequest.Content = new StringContent(JsonConvert.SerializeObject(body, defaultSerializerSettings), Encoding.UTF8, "application/json");
124+
}
125+
126+
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
65127

66-
var stream = await response.Content.ReadAsStreamAsync();
128+
var response = await SendAsync(requestUri, HttpMethod.Post, headers, AttachContent, cancellationToken: cancellationToken);
67129

68-
return SerializerHelper.Deserialize<T>(stream, jsonConverters);
130+
if (response.IsSuccessStatusCode)
131+
{
132+
return await response.ParseStreamAsync<T>(serializerSettings);
69133
}
134+
135+
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
136+
? response.ReasonPhrase
137+
: await response.Content.ReadAsStringAsync();
138+
139+
throw new NotionApiException(response.StatusCode, message);
70140
}
71141

72142
private HttpClient EnsureHttpClient()
@@ -75,8 +145,6 @@ private HttpClient EnsureHttpClient()
75145
{
76146
_httpClient = new HttpClient();
77147
_httpClient.BaseAddress = new Uri(_options.BaseUrl);
78-
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
79-
_httpClient.DefaultRequestHeaders.Add("Notion-Version", _options.NotionVersion);
80148
}
81149

82150
return _httpClient;

0 commit comments

Comments
 (0)