Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions Src/Notion.Client/Api/ApiEndpoints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Notion.Client
{
public static class ApiEndpoints
{
public static class DatabasesApiUrls
{
public static string Retrieve(string databaseId) => $"/v1/databases/{databaseId}";
public static string List() => "/v1/databases";
public static string Query(string databaseId) => $"/v1/databases/{databaseId}/query";
}

public static class UsersApiUrls
{
public static string Retrieve(string userId) => $"/v1/users/{userId}";
public static string List() => "/v1/users";
}
}
}
49 changes: 49 additions & 0 deletions Src/Notion.Client/Api/Databases/DatabasesClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Notion.Client.ApiEndpoints;

namespace Notion.Client
{
public interface IDatabasesClient
{
Task<Database> RetrieveAsync(string databaseId);
Task<PaginatedList<Page>> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters);
Task<PaginatedList<Database>> ListAsync(DatabasesListParameters databasesListParameters = null);
}

public class DatabasesClient : IDatabasesClient
{
private readonly IRestClient _client;

public DatabasesClient(IRestClient client)
{
_client = client;
}

public async Task<Database> RetrieveAsync(string databaseId)
{
return await _client.GetAsync<Database>(DatabasesApiUrls.Retrieve(databaseId));
}

public async Task<PaginatedList<Database>> ListAsync(DatabasesListParameters databasesListParameters = null)
{
var databasesListQueryParmaters = (IDatabasesListQueryParmaters)databasesListParameters;

var queryParams = new Dictionary<string, string>()
{
{ "start_cursor", databasesListQueryParmaters?.StartCursor },
{ "page_size", databasesListQueryParmaters?.PageSize }
};

return await _client.GetAsync<PaginatedList<Database>>(DatabasesApiUrls.List(), queryParams);
}

public async Task<PaginatedList<Page>> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters)
{
var body = (IDatabaseQueryBodyParameters)databasesQueryParameters;

return await _client.PostAsync<PaginatedList<Page>>(DatabasesApiUrls.Query(databaseId), body);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using static Notion.Client.ApiEndpoints;

namespace Notion.Client
{
Expand All @@ -20,26 +21,12 @@ public UsersClient(IRestClient client)

public async Task<User> RetrieveAsync(string userId)
{
try
{
return await _client.GetAsync<User>($"users/{userId}");
}
catch (Exception e)
{
return null;
}
return await _client.GetAsync<User>(UsersApiUrls.Retrieve(userId));
}

public async Task<PaginatedList<User>> ListAsync()
{
try
{
return await _client.GetAsync<PaginatedList<User>>("users");
}
catch (Exception e)
{
return null;
}
return await _client.GetAsync<PaginatedList<User>>(UsersApiUrls.List());
}
}
}
2 changes: 1 addition & 1 deletion Src/Notion.Client/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
internal class Constants
{
internal static string BASE_URL = "https://api.notion.com/v1/";
internal static string BASE_URL = "https://api.notion.com/";
internal static string DEFAULT_NOTION_VERSION = "2021-05-13";
}
}
69 changes: 0 additions & 69 deletions Src/Notion.Client/DatabasesClient.cs

This file was deleted.

35 changes: 35 additions & 0 deletions Src/Notion.Client/Extensions/HttpResponseMessageExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Notion.Client.Extensions
{
internal static class HttpResponseMessageExtensions
{
internal static async Task<T> ParseStreamAsync<T>(this HttpResponseMessage response, JsonSerializerSettings serializerSettings = null)
{
using (Stream stream = await response.Content.ReadAsStreamAsync())
{
using (StreamReader streamReader = new StreamReader(stream))
{
using (JsonReader jsonReader = new JsonTextReader(streamReader))
{
JsonSerializer serializer = null;

if (serializerSettings == null)
{
serializer = JsonSerializer.CreateDefault();
}
else
{
serializer = JsonSerializer.Create(serializerSettings);
}

return serializer.Deserialize<T>(jsonReader);
}
}
}
}
}
}
17 changes: 17 additions & 0 deletions Src/Notion.Client/NotionApiException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Net;

namespace Notion.Client
{
class NotionApiException : Exception
{
public NotionApiException(HttpStatusCode statusCode, string message) : this(statusCode, message, null)
{
}

public NotionApiException(HttpStatusCode statusCode, string message, Exception innerException) : base(message, innerException)
{
Data.Add("StatusCode", statusCode);
}
}
}
102 changes: 85 additions & 17 deletions Src/Notion.Client/RestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,38 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Notion.Client.Extensions;
using Notion.Client.http;

namespace Notion.Client
{
public interface IRestClient
{
Task<T> GetAsync<T>(string uri, Dictionary<string, string> queryParams = null);
Task<T> PostAsync<T>(string uri, object body);
Task<T> GetAsync<T>(
string uri,
IDictionary<string, string> queryParams = null,
IDictionary<string, string> headers = null,
JsonSerializerSettings serializerSettings = null,
CancellationToken cancellationToken = default);

Task<T> PostAsync<T>(
string uri,
object body,
IDictionary<string, string> queryParams = null,
IDictionary<string, string> headers = null,
JsonSerializerSettings serializerSettings = null,
CancellationToken cancellationToken = default);
}

public class RestClient : IRestClient
{
private HttpClient _httpClient;
private readonly ClientOptions _options;
private readonly List<JsonConverter> jsonConverters = new List<JsonConverter>();

private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings
private readonly JsonSerializerSettings defaultSerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
Expand All @@ -41,32 +54,89 @@ private static ClientOptions MergeOptions(ClientOptions options)
};
}

public async Task<T> GetAsync<T>(string uri, Dictionary<string, string> queryParams = null)
public async Task<T> GetAsync<T>(
string uri,
IDictionary<string, string> queryParams = null,
IDictionary<string, string> headers = null,
JsonSerializerSettings serializerSettings = null,
CancellationToken cancellationToken = default)
{
EnsureHttpClient();

uri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);

using (var stream = await _httpClient.GetStreamAsync(uri))
var response = await SendAsync(requestUri, HttpMethod.Get, headers, cancellationToken: cancellationToken);

if (response.IsSuccessStatusCode)
{
return SerializerHelper.Deserialize<T>(stream, jsonConverters);
return await response.ParseStreamAsync<T>(serializerSettings);
}

var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
? response.ReasonPhrase
: await response.Content.ReadAsStringAsync();

throw new NotionApiException(response.StatusCode, message);
}

public async Task<T> PostAsync<T>(string uri, object body)
private async Task<HttpResponseMessage> SendAsync(
string requestUri,
HttpMethod httpMethod,
IDictionary<string, string> headers = null,
Action<HttpRequestMessage> attachContent = null,
CancellationToken cancellationToken = default)
{
EnsureHttpClient();
HttpRequestMessage httpRequest = new HttpRequestMessage(httpMethod, requestUri);
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
httpRequest.Headers.Add("Notion-Version", _options.NotionVersion);

if (headers != null)
{
AddHeaders(httpRequest, headers);
}

attachContent?.Invoke(httpRequest);

return await _httpClient.SendAsync(httpRequest, cancellationToken);
}

private static void AddHeaders(HttpRequestMessage request, IDictionary<string, string> headers)
{
foreach (var header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
}

var content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, "application/json");
public async Task<T> PostAsync<T>(
string uri,
object body,
IDictionary<string, string> queryParams = null,
IDictionary<string, string> headers = null,
JsonSerializerSettings serializerSettings = null,
CancellationToken cancellationToken = default)
{
EnsureHttpClient();

using (var response = await _httpClient.PostAsync(uri, content))
void AttachContent(HttpRequestMessage httpRequest)
{
response.EnsureSuccessStatusCode();
httpRequest.Content = new StringContent(JsonConvert.SerializeObject(body, defaultSerializerSettings), Encoding.UTF8, "application/json");
}

string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);

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

return SerializerHelper.Deserialize<T>(stream, jsonConverters);
if (response.IsSuccessStatusCode)
{
return await response.ParseStreamAsync<T>(serializerSettings);
}

var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
? response.ReasonPhrase
: await response.Content.ReadAsStringAsync();

throw new NotionApiException(response.StatusCode, message);
}

private HttpClient EnsureHttpClient()
Expand All @@ -75,8 +145,6 @@ private HttpClient EnsureHttpClient()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(_options.BaseUrl);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken);
_httpClient.DefaultRequestHeaders.Add("Notion-Version", _options.NotionVersion);
}

return _httpClient;
Expand Down
Loading